天天看點

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

 本章我們介紹的兩類練習題主要是關于C語言實作資料結構的複雜度和順序表類問題。

1、異或的巧妙應用

我們刷題時候經常會遇到去重類的問題,更讓人頭疼的是有空間複雜度的限制,這就意味着我們不能重新建立一個數組用條件語句存放,是以這裡我們巧妙地借用異或語句來實作。

在做題之前我們先來看一下異或的一些特點:

1、0和任何數異或還是那個數本身。

我們用5舉例:

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

2、異或具有交換律。

比如1^2^3=1^3^2.

3、兩個相同的數異或結果為0。

因為異或的定義是相同為0,相異為1,兩個相同的數每個比特位上的值肯定一樣,是以為0.

ok,有了上面的幾個特點,我們來來一下下面的題目:

找單身狗:

一個數組中隻有兩個數字是出現一次,其他所有數字都出現了兩次。

編寫一個函數找出這兩個隻出現一次的數字。空間複雜度為O(1)。

 對于這道題,如果隻有一個數字隻出現一次,我們可以把所有數字和0異或,出現兩次的數字就會被異或成0,然後我們最後得到的數字就是隻出現一次的數字(典型單身狗問題)。

但是這題要求找出兩個隻出現一次的數字,我們怎麼辦呢?

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

比如對于這樣一組數字,我們把所有異或在一起,好像得到的數字也無法解讀處5和6,那麼我們思考可不可以分組呢?

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

 我們分成這樣的兩組,然後每一組中在内部異或,第一組就可以找出5,第二組就可以找出6,那麼如何實作這樣的分組異或呢?

這裡的分組不一定要這樣分,其實隻需要把5和6分為兩組即可。這裡我們想到,把所有數異或和0後得到的數就是5和6異或後的數字,我們從比特位的角度來看,每次右移一次然後和1進行&運算,隻要結果為1,那麼就是5和6這位比特位上數字不一樣,我們就可以通過這一特點分組。

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

然後我們利用循環曆遍每一個數,對于每一個數利用位移操作符右移上面記錄的pos個位置,然後和1&運算,隻要為1就一起異或,為0在另一組異或,兩組異或出的答案就是兩個尋找的數。

請看代碼:

#include<stdio.h>

int main()
{

	int arr[] = { 1,2,3,4,5,1,2,3,4,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	int ret = 0;
	for(i=0;i<sz;i++)
	{
		ret ^= arr[i];
	}
	int pos = 0;
	for (i = 0; i < 32; i++)
	{
		if (((ret >>i) & 1) == 1)
		{
			pos = i;
			break;
		}
	}
	int num1 = 0;
	int num2 = 0;
	for (i = 0; i < sz; i++)
	{
		if (((arr[i] >> pos) & 1) == 1)
			num1 ^= arr[i];
		else
			num2 ^= arr[i];
	}

	printf("%d %d",num1,num2);
	return 0;
}
           

2、順序表相關OJ練習題(雙指針問題)

1、力扣

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

https://leetcode.cn/problems/remove-element/

給你一個數組 nums 和一個值 val,你需要 原地 移除所有數值等于 val 的元素,并傳回移除後數組的新長度。

不要使用額外的數組空間,你必須僅使用 O(1) 額外空間并 原地 修改輸入數組。

元素的順序可以改變。你不需要考慮數組中超出新長度後面的元素。

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)
【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

這道題大家初始都會想再建立一個數組 ,但是空間複雜度的限制否決了我們這一想法。

是以我們怎麼能在目前數組上進行改動呢?這裡我們可以利用雙指針了:

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

p1和p2都先指向第一個元素,然後p1往前走,在看第二個位置和p2一不一樣,如果不一樣,p2也往前走:

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

然後p1再走,不一樣,p2再走:

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

 下一步我們發現,p1走後和p2比較,相等,那p2就不動,直到p1走到3後和p2不一樣,然後p2再走,然後把3指派給p2所在位置:

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

下面以此類推,這就是典型的快慢指針問題。

下面我們給出解答:

int removeElement(int* nums, int numsSize, int val){
int p1=0;
int p2=0;
while(p1<numsSize)
{
    if(nums[p1]!=val)
    {
        nums[p2]=nums[p1];
        p1++;
        p2++;
    }
    else if(nums[p1]==val)
    {
        p1++;
    }
}
return p2;
}
           

2、

 力扣

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

https://leetcode.cn/problems/remove-duplicates-from-sorted-array/

給你一個 升序排列 的數組 nums ,請你 原地 删除重複出現的元素,使每個元素 隻出現一次 ,傳回删除後數組的新長度。元素的 相對順序 應該保持 一緻 。

由于在某些語言中不能改變數組的長度,是以必須将結果放在數組nums的第一部分。更規範地說,如果在删除重複項之後有 k 個元素,那麼 nums 的前 k 個元素應該儲存最終結果。

将最終結果插入 nums 的前 k 個位置後傳回 k 。

不要使用額外的空間,你必須在 原地 修改輸入數組 并在使用 O(1) 額外空間的條件下完成。

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

 這道題也是去重類的問題,但是并不能用上面的異或來考慮的了,因為要保留重複的數字。我們延續雙指針的想法,p1和p2同時指向第一個元素,p1++,和p2比較,一樣的話p2就不動,不一樣p2++并把p1指向的元素值指派給p2指向的元素值。這樣就可完成了。

最後傳回的是長度,也就是p2+1。

請看代碼:

int removeDuplicates(int* nums, int numsSize){
int p1=0;
int p2=0;
while(p1<numsSize)
{if(nums[p1]==nums[p2])
{
    ++p1;
}
else if(nums[p1]!=nums[p2])
{
    ++p2;
    nums[p2]=nums[p1];
    ++p1;

}
}
return p2+1;
}
           

3、力扣

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

https://leetcode.cn/problems/merge-sorted-array/

給你兩個按 非遞減順序 排列的整數數組 nums1 和 nums2,另有兩個整數 m 和 n ,分别表示 nums1 和 nums2 中的元素數目。

請你 合并 nums2 到 nums1 中,使合并後的數組同樣按 非遞減順序 排列。

注意:最終,合并後數組不應由函數傳回,而是存儲在數組 nums1 中。為了應對這種情況,nums1 的初始長度為 m + n,其中前 m 個元素表示應合并的元素,後 n 個元素為 0 ,應忽略。nums2 的長度為 n 。

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

 這道題我們延續上面的思路的話可能導緻一些資料的覆寫,是以我們再另尋他法。

我們發現例題中nums1後面是0,我們思考可不可以從後往前曆遍呢?

【C】OJ練習題---單身狗類問題、雙指針類(資料結構順序表)1、異或的巧妙應用2、順序表相關OJ練習題(雙指針問題)

 比上面多一個指針,三指針解決這道題。

end1和end2比較大的就放在end處

然後end--,放完元素的指針也向前找。

考慮特殊情況,如果nums1元素都找完了,end1指向0位置,而end2還沒有指向0位置,那麼就把nums2剩餘的元素拷貝到nums1中就ok了。

請看代碼:

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){

int end=m+n-1;
int end1=m-1;
int end2=n-1;
while(end1>=0&&end2>=0)
{
    if(nums1[end1]<=nums2[end2])
    {
        nums1[end--]=nums2[end2--];
    }
    else if(nums1[end1]>nums2[end2])
    {
        nums1[end--]=nums1[end1--];
    }
}
while(end2>=0)
{
    nums1[end--]=nums2[end2--];
}
}
           

繼續閱讀