【绝知此事要躬行】线性表之数组OJ
[線性表–數(shù)組OJ] (C/C++實(shí)現(xiàn))
“學(xué)而時(shí)習(xí)之,不亦說乎”
哈嘍又見面啦😁,沿著上篇我們討論的線性表中的順序表,這篇博客,我們講將介紹一些數(shù)組OJ題目,學(xué)習(xí)一些關(guān)于數(shù)組的操作(騷操作)。
ps: 本節(jié)所有代碼實(shí)現(xiàn)都是leetcode上的接口型
文章目錄
- [線性表--數(shù)組OJ] (C/C++實(shí)現(xiàn))
- @[toc]
- 1.[leetcode 17.04. 消失的數(shù)字](https://leetcode-cn.com/problems/missing-number-lcci/)
- **解法一**:掃一遍數(shù)組求出實(shí)際總和`sum1`,再利用==**等差數(shù)列求和公式**==,求出期望的總和`sum2`。
- **解法二**(==位運(yùn)算==):
- 2.[ 數(shù)組中數(shù)字出現(xiàn)的次數(shù)(leetcode)](https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/)
- 3.[ 二分查找(leetcode)](https://leetcode-cn.com/problems/binary-search/)
- **3-1.target 定義在[left,right] (==左右都是閉區(qū)間的寫法==)**
- **3.2. target定義在[left,right)的開區(qū)間中(==左開右閉區(qū)的寫法==)**
- 4.[移除元素(leetcode)](https://leetcode-cn.com/problems/remove-element/)
- **解法一:暴力循環(huán),移動覆蓋** O(N^2)
- **解法二:雙指針 **O(N)
- 快慢指針
- 左右指針
- 5.[ 刪除有序數(shù)組中的重復(fù)項(xiàng)(leetcode)](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/)
- 6.[合并兩個(gè)有序數(shù)組(leetcode)](https://leetcode-cn.com/problems/merge-sorted-array/)
- 7.[輪轉(zhuǎn)數(shù)組(leetcode)](https://leetcode-cn.com/problems/rotate-array/)
- 解法一(原數(shù)組與輪轉(zhuǎn)數(shù)組間的下標(biāo)關(guān)系):
- 解法二:三次逆置(個(gè)人覺得這個(gè)方法超秀😍😍)
- [線性表--數(shù)組OJ] (C/C++實(shí)現(xiàn))
- @[toc]
- 1.[leetcode 17.04. 消失的數(shù)字](https://leetcode-cn.com/problems/missing-number-lcci/)
- **解法一**:掃一遍數(shù)組求出實(shí)際總和`sum1`,再利用==**等差數(shù)列求和公式**==,求出期望的總和`sum2`。
- **解法二**(==位運(yùn)算==):
- 2.[ 數(shù)組中數(shù)字出現(xiàn)的次數(shù)(leetcode)](https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/)
- 3.[ 二分查找(leetcode)](https://leetcode-cn.com/problems/binary-search/)
- **3-1.target 定義在[left,right] (==左右都是閉區(qū)間的寫法==)**
- **3.2. target定義在[left,right)的開區(qū)間中(==左開右閉區(qū)的寫法==)**
- 4.[移除元素(leetcode)](https://leetcode-cn.com/problems/remove-element/)
- **解法一:暴力循環(huán),移動覆蓋** O(N^2)
- **解法二:雙指針 **O(N)
- 快慢指針
- 左右指針
- 5.[ 刪除有序數(shù)組中的重復(fù)項(xiàng)(leetcode)](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/)
- 6.[合并兩個(gè)有序數(shù)組(leetcode)](https://leetcode-cn.com/problems/merge-sorted-array/)
- 7.[輪轉(zhuǎn)數(shù)組(leetcode)](https://leetcode-cn.com/problems/rotate-array/)
- 解法一(原數(shù)組與輪轉(zhuǎn)數(shù)組間的下標(biāo)關(guān)系):
- 解法二:三次逆置(個(gè)人覺得這個(gè)方法超秀😍😍)
1.leetcode 17.04. 消失的數(shù)字
題目描述:
數(shù)組nums包含從0到n的所有整數(shù),但其中缺了一個(gè)。請編寫代碼找出那個(gè)缺失的整數(shù)。你有辦法在O(n)時(shí)間內(nèi)完成嗎?
示例:nums=[3,0,4,5,7,1,2],n=7 輸出:6
解法一:掃一遍數(shù)組求出實(shí)際總和sum1,再利用==等差數(shù)列求和公式==,求出期望的總和sum2。
sum2-sum1即為缺失的數(shù)字。
代碼實(shí)現(xiàn):
class Solution { public:int missingNumber(vector<int>& nums) {int sum1=0;int sum2=(nums.size()+1)*nums.size()/2;for(int i=0;i<nums.size();i++)sum1+=nums[i];return sum2-sum1;} };解法二(位運(yùn)算):
首先,我們先來介紹一下異或(^)運(yùn)算
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
相同為0,不同為1
例如:a=12,b=9,a的二級制表示:1100,b的二進(jìn)制表示:1001
a^b為a與b按每一個(gè)每一個(gè)比特位異或,結(jié)果為:0101 ,即12^9=5
易知曉a ^ b=b ^ a,即異或操作滿足交換律
可是這有什么用咧???👀👀
由定義我們可以得到一些性質(zhì):
0 ^ a = a
a ^ a = 0
由交換律和性質(zhì)1,2 我們不難證明類似這樣的式子:a ^ b ^ c ^ b ^ c =a
借助位運(yùn)算我們可以通過如下操作把這道題解決:
因?yàn)?#xff0c;經(jīng)歷了上述操作,1-n出現(xiàn)的數(shù)字都異或了兩遍,缺失的數(shù)字僅異或了一遍
代碼實(shí)現(xiàn):
class Solution { public:int missingNumber(vector<int>& nums) {int ret=0;for(int i=0;i<nums.size();i++)ret^=nums[i];for(int i=1;i<=nums.size();i++)ret^=i;return ret;} };2. 數(shù)組中數(shù)字出現(xiàn)的次數(shù)(leetcode)
? 接著上一題異或運(yùn)算的腳步,我們來看一下這道題,對異或運(yùn)算的進(jìn)一步運(yùn)用。
題目描述:
一個(gè)整型數(shù)組 nums 里除兩個(gè)數(shù)字之外,其他數(shù)字都出現(xiàn)了兩次。請寫程序找出這兩個(gè)只出現(xiàn)一次的數(shù)字。要求時(shí)間復(fù)雜度是O(n),空間復(fù)雜度是O(1)。
示例: nums=[1,1,2,3,3,4,5,5] 輸出:[2,4]
如果順著上題我們走過的路徑,我們可能的第一個(gè)想法就是,掃一遍數(shù)組全部異或一下
但是如果記兩個(gè)只出現(xiàn)一次的數(shù)字分別為a和b ,最后我們得到的是a^b,根據(jù)這個(gè)結(jié)果我們并不能把a(bǔ)和b分出來。此時(shí)我們就遇上難題了。
讓我們看看現(xiàn)在有些什么可以利用的?
a^b的值,這個(gè)值雖然不能直接給我們a和b具體是多少,但是可以提供給我們一些關(guān)于a和b的性質(zhì)
通過a^b我們可以獲取a和b二進(jìn)制表示中不同的那個(gè)位(即a ^ b的二進(jìn)制表示中為1的那個(gè)位)
我們通過一個(gè)flag加上左移操作便可以找到那個(gè)不同的位
這樣我們便可以把數(shù)字分為兩組,進(jìn)行==分組異或==
不難證明:
代碼實(shí)現(xiàn):
class Solution { public:vector<int> singleNumbers(vector<int>& nums) {int val=0,flag=1;for(int i=0;i<nums.size();i++)val^=nums[i];while((val&flag)==0){flag<<=1;}int a=0,b=0;for(int i=0;i<nums.size();i++){if(nums[i]&flag)a^=nums[i];elseb^=nums[i];}return {a,b};} };3. 二分查找(leetcode)
題目描述:
給定一個(gè) n 個(gè)元素有序的(升序)整型數(shù)組 nums 和一個(gè)目標(biāo)值target ,寫一個(gè)函數(shù)搜索 nums 中的 target,如果目標(biāo)值存在返回下標(biāo),否則返回 -1。
3-1.target 定義在[left,right] (左右都是閉區(qū)間的寫法)
class Solution { public:int search(vector<int>& nums, int target) {int left=0,right=nums.size()-1;while(left<=right){int mid=(left+right)/2;if(nums[mid]>target)right=mid-1;else{if(nums[mid]<target)left=mid+1;elsereturn mid;}}return -1;} }; int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/23.2. target定義在[left,right)的開區(qū)間中(左開右閉區(qū)的寫法)
class Solution { public:int search(vector<int>& nums, int target) {int left=0,right=nums.size();//開區(qū)間while(left<right)//等于的情況沒有意義{int mid=(left+right)/2;//可寫成防止溢出的寫法if(nums[mid]>target)//左區(qū)間right=mid;else{if(nums[mid]<target)left=mid+1;elsereturn mid;}}return -1;} };解釋:
①.while循環(huán)中判斷條件等于就沒有意義
②.target落在左區(qū)間內(nèi)mid-1可能取到而mid不能取到,所以right=mid
4.移除元素(leetcode)
題目描述:
給你一個(gè)數(shù)組 nums 和一個(gè)值 val,你需要 原地 移除所有數(shù)值等于 val 的元素,并返回移除后數(shù)組的新長度。
不要使用額外的數(shù)組空間,你必須僅使用 O(1) 額外空間并 原地 修改輸入數(shù)組。
元素的順序可以改變。你不需要考慮數(shù)組中超出新長度后面的元素。
解法一:暴力循環(huán),移動覆蓋 O(N^2)
代碼實(shí)現(xiàn):
class Solution { public:int removeElement(vector<int>& nums, int val) {int sz=nums.size();for(int i=0;i<sz;i++){if(val==nums[i]){for(int j=i;j<sz-1;j++)nums[j]=nums[j+1];i--;sz--;}}return sz;} };!!!注意!!!
第11行的 i - -回退一步非常重要
因?yàn)槲覀儧]法判斷用來覆蓋的前一個(gè)元素的值是否等于val
這個(gè)是用來應(yīng)對val連續(xù)出現(xiàn)的情況
**解法二:雙指針 **O(N)
這里我們介紹兩種雙指針實(shí)現(xiàn)方式來完成這一題
快慢指針
思路:
這樣我們可以證明:fast走完一遍時(shí),0~slow所有的值都不等于val
class Solution { public:int removeElement(vector<int>& nums, int val) {int slow=0;for(int fast=0;fast<nums.size();fast++)if(nums[fast]!=val)nums[slow++]=nums[fast];return slow;} };左右指針
思路:
left指頭,right指尾
每次當(dāng)left指的值等于val,left指向的值是必要?jiǎng)h除的,此時(shí)我們把right指的值給left,更新right(用尾的元素覆蓋這個(gè)必要?jiǎng)h的元素,雖然不能判定尾這個(gè)元素是否等于val)
left指的值不等于val,left往前走即可
注意循環(huán)的判定條件要取到等(得判定所有的元素)
可以發(fā)現(xiàn):nums中每個(gè)元素都判定過,且0~left的所有元素都不等于val
代碼實(shí)現(xiàn):
class Solution { public:int removeElement(vector<int>& nums, int val) {int left=0,right=nums.size()-1;while(left<=right){if(val==nums[left]){nums[left]=nums[right];right--;}elseleft++;}return left;} };5. 刪除有序數(shù)組中的重復(fù)項(xiàng)(leetcode)
題目描述:
給你一個(gè) 升序排列 的數(shù)組 nums ,請你原地刪除重復(fù)出現(xiàn)的元素,使每個(gè)元素 只出現(xiàn)一次 ,返回刪除后數(shù)組的新長度。元素的 相對順序 應(yīng)該保持 一致 。
思路:
升序排列:說明數(shù)組中重復(fù)出現(xiàn)的元素一定是連續(xù)的,不可能間斷
我們用k表示刪除元素(重復(fù)元素)的個(gè)數(shù),val記錄==上一層循環(huán)的nums[i]==
在當(dāng)前循環(huán)層中如果val==nums[i],說明該層循環(huán)nums[i]為重復(fù)元素,根據(jù)我們在2中給的定義,k需要加1,val可以更新(但沒必要)
當(dāng)前循環(huán)層中的值不等于val,說明這個(gè)不是重復(fù)元素,i-k位置上的元素即為在[0,i)這個(gè)區(qū)間中第一個(gè)出現(xiàn)重復(fù)的元素(1中連續(xù)性結(jié)合2中定義得出)
示意圖
掃完一遍后,nums.size()-k即為剩下元素的個(gè)數(shù),且滿足[0,nums.size()-k]區(qū)間上·不存在重復(fù)元素。
代碼實(shí)現(xiàn):
class Solution { public:int removeDuplicates(vector<int>& nums) {int k=0,val=nums[0];for(int i=1;i<nums.size();i++){if(val==nums[i]){k++;}else{val=nums[i];nums[i-k]=nums[i];}}return nums.size()-k;} };6.合并兩個(gè)有序數(shù)組(leetcode)
題目描述:
給你兩個(gè)按 非遞減順序 排列的整數(shù)數(shù)組 nums1 和 nums2,另有兩個(gè)整數(shù)m和 n ,分別表示 nums1 和 nums2 中的元素?cái)?shù)目。
請你 合并 nums2 到 nums1 中,使合并后的數(shù)組同樣按 非遞減順序 排列。
思路:
因?yàn)閮蓚€(gè)數(shù)組都排好序,只需同時(shí)掃兩個(gè)數(shù)組,每次把小的放到ans數(shù)組中
若m==n掃完一遍后,兩數(shù)組按序插入到ans中
如果m != n最后只需把長的數(shù)組后面的元素統(tǒng)統(tǒng)放到ans中即可。
(這也是我們后續(xù)要寫的==歸并排序==的歸并操作)
代碼實(shí)現(xiàn):
class Solution { public:void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {vector<int> ans;int i=0,j=0;while(i<m&&j<n){if(nums1[i]<nums2[j])ans.push_back(nums1[i++]);elseans.push_back(nums2[j++]);}while(i<m)ans.push_back(nums1[i++]);while(j<n)ans.push_back(nums2[j++]);nums1=ans;} };7.輪轉(zhuǎn)數(shù)組(leetcode)
題目描述:
給你一個(gè)數(shù)組,將數(shù)組中的元素向右輪轉(zhuǎn) k 個(gè)位置,其中 k 是非負(fù)數(shù)。
示例:
輸入: nums = [1,2,3,4,5,6,7], k = 3
輸出: [5,6,7,1,2,3,4]
解釋:
向右輪轉(zhuǎn) 1 步: [7,1,2,3,4,5,6]
向右輪轉(zhuǎn) 2 步: [6,7,1,2,3,4,5]
向右輪轉(zhuǎn) 3 步: [5,6,7,1,2,3,4]
解法一(原數(shù)組與輪轉(zhuǎn)數(shù)組間的下標(biāo)關(guān)系):
如果原數(shù)組為nums1[i] ,輪轉(zhuǎn)后數(shù)組為nums2
可知:nums2[ i ] = nums1[ (i+k) % nums1.size() ]
代碼實(shí)現(xiàn):
class Solution { public:void rotate(vector<int>& nums, int k) {int size=nums.size();vector<int> ans(size);for(int i=0;i<size;i++)ans[(i+k)%size]=nums[i];nums=ans;} };解法二:三次逆置(個(gè)人覺得這個(gè)方法超秀😍😍)
基本操作:
reverse函數(shù) 把1,2,3,4,5完全逆置為5,4,3,2,1(雙指針實(shí)現(xiàn))
此時(shí)只需要(記原數(shù)組長度為size):
注意:k要對size取模(輪轉(zhuǎn)size次相當(dāng)于轉(zhuǎn)回來了)
代碼實(shí)現(xiàn):
class Solution { public:void reverse(vector<int>& nums, int begin, int end){int i=begin,j=end;while(i<j){int tmp=nums[i];nums[i]=nums[j];nums[j]=tmp;i++,j--;}}void rotate(vector<int>& nums, int k) {int size=nums.size();reverse(nums,0,size-k%size-1);reverse(nums,size-k%size,size-1);reverse(nums,0,size-1);} };??本篇博客關(guān)于數(shù)組OJ的內(nèi)容就到這了
總結(jié)一下:我們了解到了位運(yùn)算,二分查找,數(shù)組的插入刪除等操作的運(yùn)用,雙指針。
希望大家看完能獲得一些啟發(fā),有所收獲
本章節(jié)內(nèi)容所有源碼都會上傳到我的gitee代碼倉庫中,如有需要可前往下載,gitee鏈接:數(shù)據(jù)結(jié)構(gòu)
受作者水平所限,如果博客內(nèi)容有什么紕漏錯(cuò)誤,歡迎大家批評指正
如果大家對這些題目有什么好的方法思路,也歡迎大家來和我交流😉
后續(xù)更新鏈表oj(難度up up)和《棧與隊(duì)列》
我們下節(jié)見😊😊
總結(jié)
以上是生活随笔為你收集整理的【绝知此事要躬行】线性表之数组OJ的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开源分享-Java版超级玛丽
- 下一篇: 【有利可图网】PS教程:制作棱形岩石文字