在 C 中使用两个指针左移/右移一个数组

Posted

技术标签:

【中文标题】在 C 中使用两个指针左移/右移一个数组【英文标题】:Left Shift/ Right Shift an array using two pointers in C 【发布时间】:2021-12-05 18:00:34 【问题描述】:

我正在尝试在数组上实现左移/右移

我能够使用双循环来完成此操作。 我能得到一些帮助来提高效率吗?

这是使用 2 个循环的 LeftShift/RightShift 的工作代码。

#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;
    

我正在尝试这样的事情,它使用 2 个迭代器来解决这个问题!

这也有效!

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;
    

但是没有循环可以解决这个问题吗?还是单循环?

可以提高效率吗?

【问题讨论】:

如果代码有效并且您正在寻找改进它的建议,Code Review 是合适的地方。但请先查看codereview.meta.stackexchange.com/questions/5777/…。 不用辅助数组也可以在O(N)时间内解决这个问题;只需从索引 0 开始执行移位,直到您再次点击索引 0。对索引 1 重复,以此类推,假设索引 1 不在第一个循环中。真正的诀窍是避免起点后的冗余移位,并在所有位置都移位一次时实现。这种方法肯定是双指针方法,并且可能看起来效率低下(由于嵌套循环),但很容易证明数组中的每个元素都被访问了一次。跨度> with a single loop? 当然,在您的上一个版本中,只需执行arr-&gt;A[j] = arr-&gt;A[j+n]; 假设n 是要移动的位置数。当然,您需要限制j 以使j+n 不超出数组,然后您需要在之后从n 空白到arr-&gt;length-1。顺便说一句,i 可以在那里消除 - 它没有被引用。 【参考方案1】:

对提高效率有什么帮助?

Shift:移位一次。从O(n*length) 转到O(length)

Rotate:Shift 一次变成一个临时的。从O(n*length) 转到O(length)

首先验证n

void LeftShift_alt(struct Array *arr, int n) 
    if (n > arr->length) 
        n = arr->length;
    
    memmove(&arr->A[0], &arr->A[n], (arr->length - n)*sizeof arr->A[0]);
    memset(&arr->A[arr->length - n], 0, n * sizeof arr->A[0]);


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

如果需要,用指针代码替换 mem...()

【讨论】:

【参考方案2】:

您可以在结构中提供所有常见的访问器运算符(>、[] 等),而不是实际移动数组的内容。 (假设您使用的是支持此功能的编译器。否则,您需要创建这些 C 风格的函数。)如果有人这样做:

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

...您只需跟踪数组移动了多少。在这种情况下,他们总共向左移动了 3 个位置。当有人索引到数组时,您将累积的偏移量添加到他们的索引(以数组的大小为模)以获得他们正在寻找的条目的实际位置。这使得移位 O(1) 而不是 O(n)。如果您正在寻找一种有效的解决方案,这几乎是最好的。

【讨论】:

【参考方案3】:

CODE REVIEW之后:

在 C 中,可以通过使用单个循环来提高效率。我们可以移动它们n位置,而不是一次移动一个位置!

类似这样的:

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;
    

在实际使用中,我们会考虑使用除普通 int 之外的元素类型来进行此数组移位。事实上,它可能是一个复杂的类型,比如string,我们不应该在字符串上做原始的memcpy

在我的代码中,我将移出的元素设置为 0,这对于整数和相关类型来说是可以的,但在字符串中它不会类似地工作。

从 C++20 开始,有一个标准的 std::shift_leftstd::shift_right 可供使用。 还有std::rotate可以用来旋转元素。

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);

同样在 C++ 中,我们应该使用像 vector 这样灵活的容器来代替 struct

此外,如果我们要从两端添加和删除大量元素,那么有一个专门为此设计的容器,称为 deque(“双端队列”)。

【讨论】:

以上是关于在 C 中使用两个指针左移/右移一个数组的主要内容,如果未能解决你的问题,请参考以下文章

leetcode167. 两数之和 II - 输入有序数组(双指针)

C语言如何输入一个整数,将整数整体左移1位,且第一位移动到最后一位,输出.如输入“1234”,输出“2341”?

C和C指针小记-操作符

C语言位运算符:与或异或取反左移与右移详细介绍

经典面试题 之 数组的循环右移

C语言总结_数组知识点练习