循環不變量求解數組問題

循環不變量原理

第一次看到循環不變量是在算法導論的快排裏。一般是針對數組問題求解,需要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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章