在 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->A[j] = arr->A[j+n];
假设n
是要移动的位置数。当然,您需要限制j
以使j+n
不超出数组,然后您需要在之后从n
空白到arr->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_left
和 std::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 - 输入有序数组(双指针)