循环不变量求解数组问题

文章目录

      • 循环不变量原理
        • 快排
        • 移动 0
        • 去掉指定元素
        • 排序数组去重
        • 排序数组只保留最多2个重复的元素

循环不变量原理

第一次看到循环不变量是在算法导论的快排里。一般是针对数组问题求解,需要2个指针。

快排

快排最核心的思想是分区,也就是将给定数组分成2部分,左边比区间点小,右边比区间点大。使用循环不变量原理,以快排为例,指针 i, j 将数组分为3个部分。
其中 0 < i <= j ; i<= j < len(arr);
对所有 x < i 的元素,都有 a[x] < a[i];
对所有 i < x < j 的元素,都有 a[i] < a[x] < a[j] ;
接下来只要遍历数组,保证在遍历过程中,始终满足上述条件。
如果不满足,就通过交换来达到满足。

int partition(int *arr, int start, int end)
{
    int i = start-1;
    int j = start;
    int base = arr[end];  //基准值
    for (; j < end; ++j) {
	    //不满足循环不变量的原理,需要交换,保证不变量成立
        if (arr[j] <= base) { 
            i++;
            swap(&arr[i], &arr[j]);
        }
    }
    swap(&arr[i+1], &arr[end]);
    return i+1;
}

根据上面的思路,可以求解一些数组问题。

移动 0

将一个数组中的 0 全部移动到最后。参考快排的思想。设置2个指针,lastNotZeroPos 和 j。
lastNotZeroPos 表示数组中所有 x <= lastNotZeroPos 的元素,都不为0。
j 则表示已处理的区间。这里跟 快排不太一样的是,不满足循环不变量的时候才进行交换。这里是满足的时候就要交换。

void moveZeroes(int* nums, int numsSize) {
    int lastNotZeroPos = -1;
    for (int j=0; j < numsSize; j++){
        if (nums[j]!=0){
            nums[++lastNotZeroPos]=nums[j];
        }
    }
    return lastNotZeroPos+1;
}

去掉指定元素

跟移动 0 一样的原理

int removeElement(int* nums, int numsSize, int val) {
    int lastNotValPos = -1;
    for (int j=0; j < numsSize; j++){
        if (nums[j]!=val){
            nums[++lastNotValPos]=nums[j];
        }
    }
    return lastNotValPos+1;
}

排序数组去重

从移除0变成了移除重复项。比较的值从定值变成了动态的,即始终与循环中当前元素的前一个元素比较。

int removeDuplicates(int* nums, int numsSize) {
    int lastNotDuplicatePos = 0; //初始化为第一个元素
    for (int j=1 /*从第二个元素开始*/ ; j < numsSize; j++){
        if (nums[j]!=nums[lastNotDuplicatePos]){
            nums[++lastNotDuplicatePos]=nums[j];
        }
    }
    return lastNotDuplicatePos+1;
}

排序数组只保留最多2个重复的元素

int removeDuplicates(int* nums, int numsSize) {
    //处理边界条件
    if (numsSize<3){
        return numsSize;
    }else if (numsSize==3){
        if (nums[2]==nums[1]&&nums[1]==nums[0])
            return 2;
        else
            return 3;
    }
    
    int i = 0;
    int j = 1;
    for (int k=2; k<numsSize;k++){
        if (nums[k]!=nums[i] || nums[k]!=nums[j]){
            ++i; // equals ==> nums[++i]=nums[j];
            nums[++j] = nums[k];
        }
    }
    return j+1;
}

你可能感兴趣的:(算法)