📅  最后修改于: 2023-12-03 15:35:56.562000             🧑  作者: Mango
在 C++ 中,指针是一种非常重要的工具。除了基本的指针使用外,还有一些高级的技术可以利用指针来实现。其中一个技术就是利用两个指针来实现一些操作,如查找、排序、合并等。
在 C++ 中,指针是一种变量,它存储着另一个变量的地址。可以使用 '&' 操作符来获取变量的地址,并使用 '*' 操作符来获取指针所指向的变量的值。例如:
int x = 10;
int* ptr = &x; // ptr 指向 x 的地址
cout << *ptr << endl; // 输出 10
为了演示如何使用两个指针来实现一些操作,我们将看一个简单的例子:如何交换两个整数的值。使用两个指针实现这个操作的代码如下:
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10, y = 20;
cout << "交换前:x = " << x << ", y = " << y << endl;
swap(&x, &y);
cout << "交换后:x = " << x << ", y = " << y << endl;
return 0;
}
这里,我们将两个整数的地址作为参数传递给了 swap
函数。在函数内部,我们使用指针来获取这两个变量的值,并将它们交换。这种方法可以让我们在不使用引用时交换两个变量的值。
使用两个指针来查找一个元素在数组中的位置是一种常见的技术。该算法的实现基于两个观察结果:在一个有序的数组中,如果一个元素比目标元素小,则目标元素肯定不在这个元素的左侧;如果一个元素比目标元素大,则目标元素肯定不在这个元素的右侧。因此,在每次比较后,我们可以缩小待搜索区域的大小。
这种算法称为二分查找。使用两个指针实现二分查找的代码如下:
int binarySearch(int arr[], int n, int target) {
int left = 0, right = n - 1; // 待搜索区间的左右边界
while (left <= right) { // 当待搜索区间不为空时
int mid = left + (right - left) / 2; // 中间元素的下标
if (arr[mid] == target) { // 如果中间元素是目标元素,返回下标
return mid;
} else if (arr[mid] < target) { // 如果中间元素比目标元素小,缩小待搜索区间
left = mid + 1;
} else { // 如果中间元素比目标元素大,缩小待搜索区间
right = mid - 1;
}
}
return -1; // 如果未找到目标元素,返回 -1
}
int main() {
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int n = sizeof(arr) / sizeof(int);
int target = 5;
int index = binarySearch(arr, n, target);
if (index == -1) {
cout << "未找到 " << target << endl;
} else {
cout << target << " 在数组中的下标为 " << index << endl;
}
return 0;
}
在这个示例中,我们将待搜索区间的左右边界分别用两个指针 left
和 right
表示。在每次比较后,我们根据目标元素与中间元素的大小关系,移动左指针或右指针,从而缩小待搜索区间的大小。当左指针大于右指针时,表示待搜索区间为空,此时返回 -1 表示未找到目标元素。
使用两个指针来排序一个数组也是一种常见的技术。该算法的实现基于下面的观察结果:在一次循环中,每次交换相邻的两个元素,可以将当前最大的元素移动到数组的末尾。因此,在每次循环结束后,我们将待排序的区间缩小一个单位,直到待排序的区间为空。
这种算法称为冒泡排序。使用两个指针实现冒泡排序的代码如下:
void bubbleSort(int arr[], int n) {
for (int i = n - 1; i >= 1; i--) { // 待排序的区间的右边界
for (int j = 0; j < i; j++) { // 在待排序的区间中遍历
if (arr[j] > arr[j + 1]) { // 如果前一个元素比后一个元素大,交换它们
swap(&arr[j], &arr[j + 1]);
}
}
}
}
int main() {
int arr[] = {5, 1, 7, 3, 9, 2};
int n = sizeof(arr) / sizeof(int);
bubbleSort(arr, n);
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
在这个示例中,我们使用两个指针 i
和 j
分别表示待排序的区间的右边界和在待排序的区间中遍历的当前位置。在每次循环中,如果当前元素比后一个元素大,就将它们交换。在整个外部循环结束后,数组就会按升序排序。
使用两个指针来合并两个有序数组也是一种常见的技术。该算法的实现基于下面的观察结果:如果将两个有序数组合并为一个有序数组,我们可以从两个数组的左边开始,每次选择更小的元素放入新的数组中。因此,在选择一个元素后,我们需要移动对应数组的指针,以便在后面的选择中跳过该元素。
这种算法称为归并排序。使用两个指针实现归并排序的代码如下:
void merge(int arr[], int l, int mid, int r) {
int i = l, j = mid + 1; // 两个待合并数组的左边界
int k = 0; // 新数组的下标
int* temp = new int[r - l + 1]; // 临时数组
while (i <= mid && j <= r) { // 当两个待合并数组都不为空时
if (arr[i] <= arr[j]) { // 如果左边数组的元素更小,将其放入新数组中
temp[k++] = arr[i++];
} else { // 如果右边数组的元素更小,将其放入新数组中
temp[k++] = arr[j++];
}
}
while (i <= mid) { // 将左边数组中未被选择的元素放入新数组中
temp[k++] = arr[i++];
}
while (j <= r) { // 将右边数组中未被选择的元素放入新数组中
temp[k++] = arr[j++];
}
for (int p = 0; p < k; p++) { // 将临时数组中的元素拷贝到原数组中
arr[p + l] = temp[p];
}
delete[] temp;
}
void mergeSort(int arr[], int l, int r) {
if (l < r) {
int mid = l + (r - l) / 2;
mergeSort(arr, l, mid); // 对左半边数组进行归并排序
mergeSort(arr, mid + 1, r); // 对右半边数组进行归并排序
merge(arr, l, mid, r); // 将排序后的两个数组合并
}
}
int main() {
int arr[] = {5, 1, 7, 3, 9, 2};
int n = sizeof(arr) / sizeof(int);
mergeSort(arr, 0, n - 1);
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
在这个示例中,我们使用两个指针 i
和 j
分别表示两个待合并数组的左边界。在每次比较中,如果左边数组的元素更小,就将其放入新数组中,并移动左边数组的指针;否则,将右边数组的元素放入新数组中,并移动右边数组的指针。最后,将未被选择的元素放入新数组中,并将新数组中的元素拷贝到原数组中。
在本文中,我们介绍了使用两个指针来实现查找、排序、合并等操作的技术。这些技术在 C++ 中非常常见,并且可以帮助我们更好的利用指针。除了本文中的示例以外,还有许多其他的操作可以使用两个指针来实现,读者可以自行发掘。