双指针
双指针能够将O(n2)复杂度的算法降低为O(n),并且常见于原地算法。两个指针能够在一个for循环内完成两个for循环的工作。
同向指针
283.移动零
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void moveZeroes(int* nums, int numsSize) { int slow = 0;
for (int i = 0; i < numsSize; i++) { if (nums[i] != 0) { nums[slow] = nums[i]; slow++; } }
for (int i = slow; i < numsSize; i++) { nums[i] = 0; } }
|
在这里,slow作为慢指针,用于指示去掉0之后的“新元素”,而循环中的i
作为快指针,负责处理遍历。快指针走的比慢指针快,因此慢指针对元素做的原地处理不会和后面的快指针操作发生冲突。
27.移除元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int removeElement(int* nums, int numsSize, int val) { int slow = 0;
for (int i = 0; i < numsSize; i++) { if (nums[i] != val) { nums[slow] = nums[i]; slow++; } }
return slow; }
|
思路和上一题相同。主要判断条件都是“不等于”,因为slow要指向的对象是待储存元素(slow要重新在数组内为这些元素分配位置),而不是等于val
的元素。
26.删除有序数组中的重复项
1 2 3 4 5 6 7 8 9 10 11 12 13
| int removeDuplicates(int* nums, int numsSize) { int current_pos = 1; for (int i = 1; i < numsSize; i++) { if(nums[i] != nums[i-1]) { nums[current_pos] = nums[i]; current_pos++; } }
return current_pos; }
|
思路也是一致的,判断出当前元素是否和前一个不一致,如果不一致说明这个元素要保留,slow就会指向这个元素。
349.两个数组的交集
这题的分为两个部分:排序+双指针。双指针法只需要一次循环,前提是数组必须经过排序。排序完后的数组是升序的,两个数组分别使用两个指针从首位开始向后遍历并比较。以这两个数组为例:
排序之后的结果为
两个指针分别开始遍历,如果指针1指向值大于指针2指向值,说明指针2位置偏左,对其++;如果指针1指向值小于指针2指向值,则说明指针1偏左,对其++;如果指针1和指针2指向值大小相等,该值属于交集,提出并保存在result中。
这个做法会带来重复存入的问题,比如上面的算例中,result会先后存入两个4。为了避免该情况发生,在存入时需要做过滤,条件为result中待存入位置的前一个数不能等于当前要存入的这个数(result[k-1] != nums1[i]
。如果待存入位置是result的第一位,则不用判断,直接存入k == 0
。因为两个数组事先已经排过序,所以这个判断逻辑一定是正确的,不存在漏判断的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
int compare(const void *a, const void *b){ return *(int *)a - *(int *)b; }
int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize) { qsort(nums1, nums1Size, sizeof(int), compare); qsort(nums2, nums2Size, sizeof(int), compare); int i = 0; int j = 0; int k = 0; int* result = malloc(sizeof(int) * (nums1Size < nums2Size ? nums1Size : nums2Size));
while (i < nums1Size && j < nums2Size){ if (nums1[i] > nums2[j]){ j++; } else if (nums1[i] < nums2[j]){ i++; } else if (nums1[i] == nums2[j]){ if (k == 0 || result[k - 1] != nums1[i]){ result[k++] = nums1[i]; } i++; j++; } }
*returnSize = k; return result; }
|
排序
冒泡排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void bubble_sort(int* nums, int numsSize) { for (int i = 0 { for (int j = 0 { if (nums[j] > nums[j + 1]) { int temp = nums[j]; nums[j] = nums[j + 1]; nums[j + 1] = temp; } } } }
|
快速排序
其他
Boyer-Moore 投票算法(众数问题)
Boyer-Moore 投票算法是一种用于寻找**众数(Majority Element)**的高效算法。众数定义为一个数组中出现次数超过总长度一半的元素。该算法的时间复杂度为 O(n),空间复杂度为 O(1),非常高效。核心思想是利用计数器(count)维护当前候选元素的“优势”,如果某个元素在整个数组中出现次数超过一半,算法最终能锁定这个元素。
步骤为:
-
候选阶段:
- 从数组的第一个元素开始,假设它是众数候选人(
candidate
)。
- 遍历数组,如果当前元素与候选人相同,增加计数器(
count++
);如果不同,减少计数器(count--
)。
- 当计数器为零时,重新选择下一个元素作为新的候选人。
-
确认阶段(可选,视问题需求而定):
- 如果题目明确保证数组中一定有众数,则跳过此步骤,候选人即为众数。
- 如果不保证众数存在,需要再遍历一次数组,验证候选人的出现次数是否超过数组的一半。
169.多数元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| int majorityElement(int* nums, int numsSize) { int count = 0; int candidate = 0;
for (int i = 0; i < numsSize; i++){ if (count == 0){ candidate = nums[i]; count = 1; }else if (nums[i] == candidate){ count++; }else{ count--; } }
return candidate; }
|