简体   繁体   中英

Left Shift/ Right Shift an array using two pointers in C

I am trying to implement a Left shift/ Right Shift on arrays .

I was able to accomplish this using double loops. Can I get some help to improve the efficiency?

This is the working code for LeftShift/RightShift which is using 2 loops.

#include <iostream>
#include <stdlib.h>
#include <stdio.h> 

struct Array
{
    int A[10];
    int size;
    int length;
};

void Display(struct Array arr)
{
    printf("\nElements are : \n");
    for(int i = 0;i<arr.length;i++)
        printf("%d ", arr.A[i]);
}

// Left Shift-------------------------------------------------------------------------------------------------------
void LeftShift1(struct Array *arr, int n)    //n is the number of shifts
{
    int temp = arr->A[0];
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<arr->length-1; j++)
        {
            arr->A[j] = arr->A[j+1];
        }
        arr->A[arr->length-1] = 0;
    }
}

//Right Shift-------------------------------------------------------------------------------------------------------
void RightShift(struct Array *arr, int n)    //n is the number of shifts
{
    for(int i = 0; i<n; i++)
    {
        for(int j=arr->length-1; j>0; j--)
        {
            arr->A[j] = arr->A[j-1];
        }
        arr->A[0] = 0;
    }
    
}

int main()
{
    struct Array arr={{1,2,3,4,5},10,5};
    
    LeftShift1(&arr, 2);
    //RightShift(&arr, 1);

    Display(arr);

    return 0;
}    

I'm trying something like this which uses 2 iterators to solve this problem!

This is also working!

void LeftShift2(struct Array *arr, int n)
{
    for(int k=0; k<n; k++)
    {
        int i,j;
        for(i=0, j=0; j<arr->length-1; i++, j++)
        {
            arr->A[j] = arr->A[j+1];
        }
        arr->A[arr->length-1] = 0;
    }
}

But can this be solved without loops? OR with a single loop?

Can this be made more efficient?

some help to improve the efficiency?

Shift: Shift once. Go from O(n*length) to O(length) .

Rotate: Shift once into a temporary. Go from O(n*length) to O(length) .

Qualify size amount first.

void LeftShift_alt(struct Array *arr, int n) {
  if (n > arr->length) {
    n = arr->length;
  }
  memmove(arr, arr + n, sizeof *arr * (arr->length - n));
  memset(arr + n, 0, sizeof *arr * n);
}

void LeftRotate_alt(struct Array *arr, int n) {
  if (arr->length > 0) {
    n %= arr->length;
    if (n > 0) {
      int temp[n];
      memcpy(temp, arr, sizeof temp);
      memmove(arr, arr + n, sizeof *arr * (arr->length - n));
      memcpy(arr + n, temp, sizeof temp);
    }
  }
}

Replace mem...() with pointer code if desired.

Rather than actually moving the contents of the array around, you could provide all the common accessor operators (<<, >>, [], etc.) in the struct. (Assuming you're using a compiler that supports this. Otherwise, you'll need to create these functions C-style.) If someone did this:

my_array <<= 5;
my_array >>= 2;

...you'd simply keep track of how much the array has been shifted. In this case, they've shifted a total of 3 positions to the left. When someone indexes into the array, you add the accumulated offset to their index (modulo the size of the array) to get the actual location of the entry they're looking for. This makes shifts O(1) instead of O(n). If you're looking for an efficient solution, this is about as good as it gets.

After CODE REVIEW :

In C, efficiency can be improved by using a single loop. Instead of shifting elements one position at a time we can move them n positions!

Something like this:

void LeftShift1(struct Array* arr, unsigned int n) {
    if (n > arr->length) {
        n = arr->length;
    }
    for (unsigned int i = 0; i < arr->length; i++) {
        if (i + n < arr->length)
            arr->A[i] = arr->A[i + n];
        else
            arr->A[i] = 0;
    }
}

In practical usage, we would want to consider doing this array shifting with element types other than a plain int . In fact, it may be a complex type like a string and we should not do raw memcpy on string.

In my code, I was setting the shifted-out elements to 0 which was OK for an integer and related types, but it won't work similarly in string.

As of C++20, there is a standard std::shift_left and std::shift_right ready to use. There also exists std::rotate which can be used to rotate the elements.

int arr[] = {1,2,3,4,5};
using std::ranges::begin;
using std::ranges::end;
std::shift_left (begin(arr),end(arr),2);
Display(arr);

Also in C++, we should use flexible container like vector in place of struct !

Also If we are doing a lot of adding and removing elements from both ends then there is a container specifically designed for that called deque ("doubly ended queue").

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM