2020.2.16
☟☟☟
(1)1.兩數之和
題目:給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和爲目標值的那 兩個 整數,並返回他們的數組下標。
你可以假設每種輸入只會對應一個答案。但是,你不能重複利用這個數組中同樣的元素。
示例:
給定 nums = [2, 7, 11, 15], target = 9
因爲 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
暴力法:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
// int i,j;
int len=nums.size();
for(int i=0;i<len-1;i++){
for(int j=i+1;j<len;j++){
if(nums[i]+nums[j]==target){
return{i,j};//如果找到,就返回
}
}
}
return {};//沒有找到返回空數組
}
};
Hash表
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
map<int,int> a;//提供一對一的hash
vector<int> res;//用來承載結果
for(int i=0;i<nums.size();i++)
{ //先找再存,這樣有先後關係,在已經存的元素中去找可以湊成2sum對的元素,防止同一個數被使用兩次
if(a.count(target-nums[i]))
{
res.push_back(a[target-nums[i]]);
res.push_back(i);
break;
}
a[nums[i]]=i;//反過來放入map中,用來獲取結果下標
}
return res;
};
};
☟☟☟
(2)167 兩數之和||-輸入有序數組
167 Two Sum II - Input array is sorted
給定一個已按照升序排列 的有序數組,找到兩個數使得它們相加之和等於目標數。
函數應該返回這兩個下標值 index1 和 index2,其中 index1 必須小於 index2。
說明:
返回的下標值(index1 和 index2)不是從零開始的。
你可以假設每個輸入只對應唯一的答案,而且你不可以重複使用相同的元素。
示例:
輸入: numbers = [2, 7, 11, 15], target = 9
輸出: [1,2]
解釋: 2 與 7 之和等於目標數 9 。因此 index1 = 1, index2 = 2 。
思路:雙指針,因爲是有序的。
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
//vector<int> res;//用vector保存也行
int l=0;
int r=numbers.size()-1;
while(l<r){//從兩邊開始掃描
if(numbers[l]+numbers[r]<target){
l++;
}
else if(numbers[l]+numbers[r]>target){
r--;
}
else{
return {l+1,r+1};
}
}
return {};
}
};
hash,不快
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
map<int,int> mp;
vector<int> res;
int len=nums.size();
for(int i=0; i<len; i++){
if(mp.count(target-nums[i])){
res.push_back(mp[target-nums[i]]+1);
res.push_back(i+1);
}
mp[nums[i]]=i;
}
return res;
}
};
☟☟☟
(3)15.三數之和
給定一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?找出所有滿足條件且不重複的三元組。
注意:答案中不可以包含重複的三元組。
示例:
給定數組 nums = [-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合爲:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路:排序之後用雙指針,轉化爲兩數之和問題。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
vector<int>tmp;
int len=nums.size();
if(len<=2) return res;
sort(nums.begin(),nums.end());//排序
for(int i=0;i<len-2;i++){
if(nums[i]>0){
return res;
}
if(i>0 && nums[i]==nums[i-1]){
continue;
}
int left=i+1,right=len-1;
while(left<right){
if(nums[i]+nums[left]+nums[right]==0){
tmp.push_back(nums[i]);
tmp.push_back(nums[left]);
tmp.push_back(nums[right]);
res.push_back(tmp);
tmp.clear();
while(left<right && nums[left]==nums[left+1]) left++;
while(left<right && nums[right]==nums[right-1]) right--;
//left++;
right--;
}
else if(nums[i]+nums[left]+nums[right]<0){
left++;
}
else{
right--;
}
}
}
return res;
}
};
☺☺☺
(4)16.最接近的三數之和
給定一個包括 n 個整數的數組 nums 和 一個目標值 target。找出 nums 中的三個整數,使得它們的和與 target 最接近。返回這三個數的和。假定每組輸入只存在唯一答案。
例如,給定數組 nums = [-1,2,1,-4], 和 target = 1.
與 target 最接近的三個數的和爲 2. (-1 + 2 + 1 = 2).
思路:雙指針。排序後! 掃描a[i],後面在用left和right首尾兩指針掃描
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
int len=nums.size();
if(len<3) return 0;
sort(nums.begin(),nums.end());
int sum=nums[0]+nums[1]+nums[2];
for(int i=0;i<len-2;i++){
int left=i+1,right=len-1;
while(left<right){
int temp=nums[i]+nums[left]+nums[right];
if(abs(temp-target)<abs(sum-target)){
sum=temp;
}
if(temp<target){
left++;
}
else {
right--;
}
}
}
return sum;
}
};
☺☺☺
(5)18.四數之和
給定一個包含 n 個整數的數組 nums 和一個目標值 target,判斷 nums 中是否存在四個元素 a,b,c 和 d ,使得 a + b + c + d 的值與 target 相等?找出所有滿足條件且不重複的四元組。
注意:
答案中不可以包含重複的四元組。
示例:
給定數組 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
滿足要求的四元組集合爲:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
思路:排序,雙指針;參照三數之和
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> res;
int len=nums.size();
if(len<4) return res;
sort(nums.begin(),nums.end());//排序
for(int i=0;i<len-3;i++){
if(i>0 && nums[i]==nums[i-1]) continue; //重複
for(int j=i+1; j<len-2;j++){
if(j>i+1 && nums[j]==nums[j-1]) continue; //重複
int left=j+1, right=len-1;
while(left<right){
if(nums[i]+nums[j]+nums[left]+nums[right]==target){
res.push_back({nums[i],nums[j],nums[left],nums[right]});
while(left<right && nums[left+1]==nums[left]) left++; //重複
while(left<right && nums[right-1]==nums[right]) right--; //重複
left++;
}
else if(nums[i]+nums[j]+nums[left]+nums[right]<target){
left++;
}
else{
right--;
}
}
}
}
return res;
}
};
也可以定義一個set,可以避免重複項,記得最後要轉化回去
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
//vector<vector<int>> res;
set<vector<int>> res;
int len=nums.size();
if(len<4) return vector<vector<int>>(); //或者用{{}}
sort(nums.begin(),nums.end());//排序
for(int i=0;i<len-3;i++){
//if(i>0 && nums[i]==nums[i-1]) continue;
for(int j=i+1; j<len-2;j++){
//if(j>i+1 && nums[j]==nums[j-1]) continue;
int left=j+1, right=len-1;
while(left<right){
if(nums[i]+nums[j]+nums[left]+nums[right]==target){
vector<int> tmp{nums[i], nums[j], nums[left], nums[right]};
res.insert(tmp); //用set,當有重複結果時,插入會失敗
left++;
}
else if(nums[i]+nums[j]+nums[left]+nums[right]<target){
left++;
}
else{
right--;
}
}
}
}
return vector<vector<int>>(res.begin(), res.end()); //由set轉化爲vector輸出
}
};
☺☺☺
(6)217.存在重複元素
給定一個整數數組,判斷是否存在重複元素。
如果任何值在數組中出現至少兩次,函數返回 true。如果數組中每個元素都不相同,則返回 false。
示例 1:
輸入: [1,2,3,1]
輸出: true
示例 2:
輸入: [1,2,3,4]
輸出: false
示例 3:
輸入: [1,1,1,3,3,4,3,2,4,2]
輸出: true
法1:哈希,判斷key值是否大於1
//unordered_map<int,int> map;//無序map更快
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
map<int,int> p;
for(int i=0;i<nums.size();i++){
p[nums[i]]++; //注意後寫if條件
if(p[nums[i]]>1){
return true;
}
}
return false;
}
};
法2.利用集合,與原數組比較大小
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
set<int> st (nums.begin(),nums.end()); //初始化
return (st.size()==nums.size()) ? false:true;
//return nums.size()>st.size();
}
};
法3.排序,比較前後兩個元素是否相等,來判斷是否存在重複元素
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
int len=nums.size();
if(len<2) return false;
sort(nums.begin(),nums.end());
for(int i=1; i<nums.size();i++){
if(nums[i]==nums[i-1]){ //注意從1開始
return true;
}
}
return false;
}
};
☺☺☺
(7)26. 刪除排序數組中的重複項
給定一個排序數組,你需要在原地刪除重複出現的元素,使得每個元素只出現一次,返回移除後數組的新長度。
不要使用額外的數組空間,你必須在原地修改輸入數組並在使用 O(1) 額外空間的條件下完成。
示例 1:
給定數組 nums = [1,1,2],
函數應該返回新的長度 2, 並且原數組 nums 的前兩個元素被修改爲 1, 2。
你不需要考慮數組中超出新長度後面的元素。
示例 2:
給定 nums = [0,0,1,1,1,2,2,3,3,4],
函數應該返回新的長度 5, 並且原數組 nums 的前五個元素被修改爲 0, 1, 2, 3, 4。
你不需要考慮數組中超出新長度後面的元素。
方法:雙指針法(覆蓋法);相等時不覆蓋,不等時覆蓋
設置兩個指針,一個慢指針i,一個快指針j,當nums[i]!=nums[j],將nums[i+1]=nums[j]。
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if(nums.size()==0) return 0;
int i=0;
for(int j=1; j<nums.size(); j++){
if(nums[j]!=nums[i]){
++i;
nums[i]=nums[j];
}
}
return i+1;
}
};
☺☺☺
(8)80. 刪除排序數組中的重複項 II 【中等】
給定一個排序數組,你需要在原地刪除重複出現的元素,使得每個元素最多出現兩次,返回移除後數組的新長度。
不要使用額外的數組空間,你必須在原地修改輸入數組並在使用 O(1) 額外空間的條件下完成。
示例 :
給定 nums = [1,1,1,2,2,3],
函數應返回新長度 length = 5, 並且原數組的前五個元素被修改爲 1, 1, 2, 2, 3 。
思路:原地刪除,用雙指針!快指針:遍歷整個數組;慢指針:記錄可以覆寫數據的位置;
當數組的長度小於等於 2 時,不需要操作,直接返回原數組即可。
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int len=nums.size();
if(len<=2) return len;
int i=1;
for(int j=2; j<len; j++){
if(nums[j]!=nums[i-1]){
i++;
nums[i]=nums[j];
}
}
return i+1;
}
};
☺☺☺
(9)349. 兩個數組的交集
給定兩個數組,編寫一個函數來計算它們的交集。
示例 1:
輸入: nums1 = [1,2,2,1], nums2 = [2,2]
輸出: [2]
示例 2:
輸入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
輸出: [9,4]
說明:
輸出結果中的每個元素一定是唯一的。
我們可以不考慮輸出結果的順序。
法1:查找,用set避免重複
注意:迭代器 end 指向尾元素的“下一個位置”!
O(n) ,O(n)
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> st1(nums1.begin(),nums1.end()); //用unordered_set對nums1的元素去重
unordered_set<int> st2(nums2.begin(),nums2.end());//用unordered_set對nums2的元素去重
vector<int> res;
for(int i:st2){//set不能用下標運算符,所以寫成**a:st2**,遍歷st2中元素
if(st1.find(i)!=st1.end()){ //必須寫**!=st1.end()**
res.push_back(i);
}
}
return res;
}
};
法2:同法1
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
unordered_set<int> m(nums1.begin(), nums1.end()); //將第一個數組的元素建立一個unordered_set
vector<int> res;//建立關於結果的vector
for(int a:nums2) //set不能用下標運算符,所以寫成**a:nums2**,遍歷nums中元素
{
if( m.count(a)) //查找了m中是否存在a,
{
res.push_back(a);//在vector res的末尾加入a
m.erase(a);//去除該數,//**刪除的是m中的元素**
//這裏不理解, a是迭代器嗎???????和(10)法1比較
//因爲在m中刪除,所以變成迭代器了???
}
}
return res;
}
};
自我理解上面問題:set裏面不需要,vector需要抽象出一個迭代器。
法3:排序後再找
空間換時間
O(nlogn),O(1)
**注意:獲取首元素和尾元素最直接的方法是調用 front 和 back **
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
vector<int> res;
sort(nums1.begin(),nums1.end());
sort(nums2.begin(),nums2.end());
int i=0,j=0;
while (i<nums1.size() && j<nums2.size()){
if(nums1[i]<nums2[j]){
i++;
}
else if(nums1[i]>nums2[j]){
j++;
}
else{//==
if(res.empty() || res.back()!=nums1[i]){
res.push_back(nums1[i]);
}
i++;
j++;
}
}
return res;
}
};
細節注意:c.erase(p)是刪除迭代器p所指的元素,所以說p是一個迭代器!
set<int> iset={1,2,3,4,5,6,7,8,9,10};
iset.find(1); //返回一個迭代器!!!,指向key==1的元素
iset.find(11); //返回一個迭代器!!!,其值等於iset.end()
iset.count(1); //返回1
iset.count(11); //返回0
☺☺☺
(10)350. 兩個數組的交集 II
給定兩個數組,編寫一個函數來計算它們的交集。
示例 1:
輸入: nums1 = [1,2,2,1], nums2 = [2,2]
輸出: [2,2]
示例 2:
輸入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
輸出: [4,9]
說明:
輸出結果中每個元素出現的次數,應與元素在兩個數組中出現的次數一致。
我們可以不考慮輸出結果的順序。
法1:直接在數組上查找,查找完之後刪除
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
vector<int>::iterator it; //必須先定義迭代器it
vector<int> res;
for(int i:nums1){// i是一個值
it=find(nums2.begin(),nums2.end(),i); //it是一個迭代器
if(it!=nums2.end()){
res.push_back(i); // 或者 res.push_back(*it);
nums2.erase(it); //it是迭代器
}
}
return res;
}
};
排序後再找
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
if(nums1.empty() || nums2.empty()) return {};
vector<int> res;
sort(nums1.begin(),nums1.end());
sort(nums2.begin(),nums2.end());
int i=0,k=0;
while( i<nums1.size() && k<nums2.size() )
{
if(nums1[i]==nums2[k])
{
res.push_back(nums1[i]);
i++;
k++;
}
else if(nums1[i]>nums2[k])
k++;
else
i++;
}
return res;
}
};
法3:hash表
**思路:此題可以看成是一道傳統的映射題(map映射),爲什麼可以這樣看呢,因爲我們需找出兩個數組的交集元素,同時應與兩個數組中出現的次數一致。這樣就導致了我們需要知道每個值出現的次數,所以映射關係就成了<元素,出現次數>,所以我們可以首先統計數組1中所有元素的出現次數。然後再遍歷數組2,如果數組2中的元素在map中存在(出現次數大於0),該元素就是一個交集元素,我們就將其存入返回數組中並且將map中該元素的出現次數減一即可.
**
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
map<int,int> map;
vector<int> res;
for(int i=0; i<nums1.size(); i++){
map[nums1[i]]++;
}
for(int i=0; i<nums2.size(); i++){
if(map[nums2[i]]>0){
res.push_back(nums2[i]);
map[nums2[i]]--;
}
}
return res;
}
};
☺☺☺
(11)228. 彙總區間
給定一個無重複元素的有序整數數組,返回數組區間範圍的彙總。
示例 1:
輸入: [0,1,2,4,5,7]
輸出: [“0->2”,“4->5”,“7”]
解釋: 0,1,2 可組成一個連續的區間; 4,5 可組成一個連續的區間。
示例 2:
輸入: [0,2,3,4,6,8,9]
輸出: [“0”,“2->4”,“6”,“8->9”]
解釋: 2,3,4 可組成一個連續的區間; 8,9 可組成一個連續的區間。
思路:雙指針
因爲題目強調的是連續的區間,所以可用如下方法。第二指針結束位置:區間右端
class Solution {
public:
vector<string> summaryRanges(vector<int>& nums) {
vector<string> res;
if(nums.empty()){
return res;
}
for(int i=0; i<nums.size();){ //注意寫 ;
int left=i;
int right=i;
while(right+1<nums.size() && nums[right+1]==nums[right]+1){
right++;
}
if(right-left==0){
res.push_back(to_string(nums[right]));
}
else if(right-left>0){
res.push_back(to_string(nums[left])+"->"+ to_string(nums[right]));
}
i=right+1;
}
return res;
}
};
注意:寫成這樣會超時:
while(right+1<nums.size() && nums[right+1]-nums[right]==1){
right++;
}
方法同上:
class Solution {
public:
vector<string> summaryRanges(vector<int>& nums) {
vector<string> res;
if(!nums.empty()){
long i = 0, j = 0;
while(j < nums.size()){
while(j < nums.size()-1 && nums[j+1] == nums[j]+1) j++;
if(j == i){
res.push_back(to_string(nums[i]));
}
if(j > i) res.push_back(to_string(nums[i])+"->"+to_string(nums[j]));
i = j + 1;
j = i;
}
}
return res;
}
};
☺☺☺
(12)334. 遞增的三元子序列
給定一個未排序的數組,判斷這個數組中是否存在長度爲 3 的遞增子序列。
數學表達式如下:
如果存在這樣的 i, j, k, 且滿足 0 ≤ i < j < k ≤ n-1,
使得 arr[i] < arr[j] < arr[k] ,返回 true ; 否則返回 false 。
說明: 要求算法的時間複雜度爲 O(n),空間複雜度爲 O(1) 。
示例 1:
輸入: [1,2,3,4,5]
輸出: true
示例 2:
輸入: [5,4,3,2,1]
輸出: false
注意:可以是不連續的數,比如[2,1,5,0,4,6]也返回true
思路:雙指針。。。長度爲3意思是連續的3個數,[2,1,5,0,4,6]結果是[0,4,6]
m1, m2保存兩個較小數,找出一個同時大於m1和m2的數即返回。
時間複雜度:O(n)
空間複雜度:O(1)
class Solution {
public:
bool increasingTriplet(vector<int>& nums) {
int m1 = INT_MAX, m2 = INT_MAX;
for (auto a : nums) {
if (a<=m1) m1 = a; //a爲掃描過程中遇到的最小數,是第一個數的候選
else if (a<=m2) m2 = a;//當a 大於m1,a可能是第二或第三個數
else return true; // m1<m2<a,說明這樣的三元子序列存在
}
return false;
}
};
☺☺☺
(13)66. 加一
給定一個由整數組成的非空數組所表示的非負整數,在該數的基礎上加一。
最高位數字存放在數組的首位, 數組中每個元素只存儲單個數字。
你可以假設除了整數 0 之外,這個整數不會以零開頭。
示例 1:
輸入: [1,2,3]
輸出: [1,2,4]
解釋: 輸入數組表示數字 123。
示例 2:
輸入: [4,3,2,1]
輸出: [4,3,2,2]
解釋: 輸入數組表示數字 4321。
/*
問題:數組加一,高位在前,低位在後
要考慮進位情況
*/
class Solution {
public:
vector<int> plusOne(vector<int>& digits) {
//123 129 199 999
for(int i=digits.size()-1; i>=0; i--){//從後往前掃描
if(digits[i]==9){//如果爲9,置0,下次進位
digits[i]=0;
}
else{
digits[i]++;
return digits;//不爲9就加一,退出
}
}
digits[0]=1;//如果最高位有進位,置1
digits.push_back(0); //補充一位
return digits;
}
};
☟☟☟
(14)283. 移動零
知識點:c.erase(p) 刪除迭代器p所指定的元素,返回一個指向被刪除元素之後的迭代器(注意是 之後!);若p指向尾元素,則返回尾後迭代器。
c.push_back(t) 在c的尾部創建一個值爲t的元素,返回void;
c.insert(p,t) 在迭代器p指向的元素之前創建一個值爲t的元素。返回指向新添加的元素的迭代器; c.inert(p,n,t) 在迭代器p指向的元素之前插入n個值爲t的元素。返回指向新添加的第一個元素的迭代器。
題目:
給定一個數組 nums,編寫一個函數將所有 0 移動到數組的末尾,同時保持非零元素的相對順序。
示例:
輸入: [0,1,0,3,12]
輸出: [1,3,12,0,0]
說明:
必須在原數組上操作,不能拷貝額外的數組。
儘量減少操作次數
法1:遍歷數組,只要遇到0則刪除,count加1(計0的個數),最後在末尾補上count個0即可。因爲使用了迭代器,要注意的是避免迭代器失效。
方法不咋地,但是能學會運用不少東西啊,比如迭代器。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int count=0;
for (auto it=nums.begin(); it!=nums.end();){
if(*it==0){
it=nums.erase(it);
count++;
}
else it++;
}
while(count>0){
nums.push_back(0);
//nums.insert(nums.end(),0);
count--;
}
//nums.insert(nums.end(),count,0);//可以用這個代替上面的while循環
}
};
法2:雙指針
✘錯誤代碼
原因:int i 定義到了for循環裏面,是局部變量,當第一個for循環結束,i就不存在了。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
//int i=0;
for(int i=0; i<nums.size() && nums[i]!=0; ++i);//
for(int j=i+1; j<nums.size(); j++){
if(nums[j]!=0){
swap(nums[i],nums[j]);
++i;
}
}
}
};
**正確代碼,定義全局變量i **
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int i=0;
for(i=0; i<nums.size() && nums[i]!=0; ++i);////找到第一個0;
for(int j=i+1; j<nums.size(); j++){//將第一個零之後的第一個非零數字與該0交換
if(nums[j]!=0){
swap(nums[i],nums[j]);
++i;
}
}
}
};
☟☟☟
(15)36. 有效的數獨 【中等】
判斷一個 9x9 的數獨是否有效。只需要根據以下規則,驗證已經填入的數字是否有效即可。
數字 1-9 在每一行只能出現一次。
數字 1-9 在每一列只能出現一次。
數字 1-9 在每一個以粗實線分隔的 3x3 宮內只能出現一次。
上圖是一個部分填充的有效的數獨。
數獨部分空格內已填入了數字,空白格用 ‘.’ 表示。
示例 1:
輸入:
[
["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
輸出: true
示例 2:
輸入:
[
["8","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
輸出: false
解釋: 除了第一行的第一個數字從 5 改爲 8 以外,空格內其他數字均與 示例1 相同。
但由於位於左上角的 3x3 宮內有兩個 8 存在, 因此這個數獨是無效的。
說明:
一個有效的數獨(部分已被填充)不一定是可解的。
只需要根據以上規則,驗證已經填入的數字是否有效即可。
給定數獨序列只包含數字 1-9 和字符 '.' 。
給定數獨永遠是 9x9 形式的。
法1:暴力!先按行排查,再按列排查,最後按3*3排查。具體實現採用了輔助數組a[10]來統計0-9出現的次數,若a[x]>1,則返回false。
因爲要存放數字1-9,最大要到9的位置,所以定義數組大小爲a[10],下標爲0~9
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
//先按照行排查
for(int i=0;i<9;++i){
int a[10]={0,0,0,0,0,0,0,0,0,0};
for(int j=0;j<9;++j){
if(board[i][j]=='.') continue;
a[board[i][j]-'0']+=1;
if(a[board[i][j]-'0']>1) return false;
}
}
//按列排查
for(int j=0;j<9;++j){
int a[10]={0,0,0,0,0,0,0,0,0,0};
for(int i=0;i<9;++i){
if(board[i][j]=='.') continue;
a[board[i][j]-'0']+=1;
if(a[board[i][j]-'0']>1)return false;
}
}
//按照3*3排查
for(int i=0;i<9;i=i+3){
for(int j=0;j<9;j=j+3){
int a[10]={0,0,0,0,0,0,0,0,0,0};
for(int x=i;x<i+3;x++){
for(int y=j;y<j+3;y++){
if(board[x][y]=='.') continue;
a[board[x][y]-'0']+=1;
if(a[board[x][y]-'0']>1)return false;
}
}
}
}
return true;
}
};
法2:這個解法有點燒腦!!!
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
int row[9][10] = {0};// 哈希表存儲每一行的每個數是否出現過,默認初始情況下,每一行每一個數都沒有出現過
// 整個board有9行,第二維的維數10是爲了讓下標有9,和數獨中的數字9對應。
int col[9][10] = {0};// 存儲每一列的每個數是否出現過,默認初始情況下,每一列的每一個數都沒有出現過
int box[9][10] = {0};// 存儲每一個box的每個數是否出現過,默認初始情況下,在每個box中,每個數都沒有出現過。整個board有9個box。
for(int i=0; i<9; i++){
for(int j = 0; j<9; j++){
// 遍歷到第i行第j列的那個數,我們要判斷這個數在其所在的行有沒有出現過,
// 同時判斷這個數在其所在的列有沒有出現過
// 同時判斷這個數在其所在的box中有沒有出現過
if(board[i][j] == '.') continue;
int curNumber = board[i][j]-'0';
if(row[i][curNumber]) return false;
if(col[j][curNumber]) return false;
if(box[j/3 + (i/3)*3][curNumber]) return false;
row[i][curNumber] = 1;// 之前都沒出現過,現在出現了,就給它置爲1,下次再遇見就能夠直接返回false了。
col[j][curNumber] = 1;
box[j/3 + (i/3)*3][curNumber] = 1;
}
}
return true;
}
};
☟☟☟
(16)48. 旋轉圖像
給定一個 n × n 的二維矩陣表示一個圖像。
將圖像順時針旋轉 90 度。
說明:
你必須在原地旋轉圖像,這意味着你需要直接修改輸入的二維矩陣。請不要使用另一個矩陣來旋轉圖像。
示例 1:
給定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋轉輸入矩陣,使其變爲:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
示例 2:
給定 matrix =
[
[ 5, 1, 9,11],
[ 2, 4, 8,10],
[13, 3, 6, 7],
[15,14,12,16]
],
原地旋轉輸入矩陣,使其變爲:
[
[15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]
對角線座標關係:
順時針旋轉矩陣:先上下翻轉,再沿對角線翻轉
//問題:旋轉圖像(要求:原地順時針旋轉90°)
/*
* 順時針旋轉方法:先上下翻轉,再沿對角線翻轉
* clockwise rotate
* first reverse up to down, then swap the symmetry
* 1 2 3 7 8 9 7 4 1
* 4 5 6 => 4 5 6 => 8 5 2
* 7 8 9 1 2 3 9 6 3
*/
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
reverse(matrix.begin(), matrix.end()); //stl中翻轉數組,對每列reverse,直接reverse matrix即可
for(int i=0; i<matrix.size(); i++){//只需對一半元素執行操作
for(int j=i+1; j<matrix[0].size(); j++){
swap(matrix[i][j],matrix[j][i]);
}
}
}
};
逆時針旋轉矩陣:先左右翻轉,再沿對角線翻轉
/*
* * 逆時針旋轉方法:先左右翻轉,再沿對角線翻轉
* anticlockwise rotate
* first reverse left to right, then swap the symmetry
* 1 2 3 3 2 1 3 6 9
* 4 5 6 => 6 5 4 => 2 5 8
* 7 8 9 9 8 7 1 4 7
*/
void anti_rotate(vector<vector<int> > &matrix) {
for (auto vi : matrix) reverse(vi.begin(), vi.end()); //對每行reverse,故要遍歷matrix,reverse內部容器
for (int i = 0; i < matrix.size(); ++i) {
for (int j = i + 1; j < matrix[i].size(); ++j)
swap(matrix[i][j], matrix[j][i]);
}
}
☟☟☟
(17)面試題57. 和爲s的兩個數字
輸入一個遞增排序的數組和一個數字s,在數組中查找兩個數,使得它們的和正好是s。如果有多對數字的和等於s,則輸出任意一對即可。
示例 1:
輸入:nums = [2,7,11,15], target = 9
輸出:[2,7] 或者 [7,2]
示例 2:
輸入:nums = [10,26,30,31,47,60], target = 40
輸出:[10,30] 或者 [30,10]
思路:雙指針
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> res;
int left=0, right=nums.size()-1;
while(left < right){
if(nums[left]+nums[right]==target){
res.push_back(nums[left]);
res.push_back(nums[right]);
break; //找到了, 就彈出
}
else if(nums[left]+nums[right] < target) left++;
else right--;
}
return res;
}
};
如果題目要求輸出乘積最小的,如下:
題目描述
輸入一個遞增排序的數組和一個數字S,在數組中查找兩個數,使得他們的和正好是S,如果有多對數字的和等於S,輸出兩個數的乘積最小的。
則,思路也一樣,因爲:
不要被題目誤導了!證明如下,清晰明瞭:
//輸出兩個數的乘積最小的。這句話的理解?
假設:若b>a,且存在,
a + b = s;
(a - m ) + (b + m) = s
則:(a - m )(b + m)=ab - (b-a)m - m*m < ab;說明外層的乘積更小
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
vector<int> result;
int size=array.size();
int left=0;
int right=size-1;
while(right>left){
if(array[left]+array[right]==sum){
result.push_back(array[left]);
result.push_back(array[right]);
break;
}
else if(array[left]+array[right]<sum){
left++;
}
else{
right--;
}
}
return result;
}
};
也貼上這個代碼吧,萬一人家要求輸出乘積最大的呢。變一下下面的符號就行。
//雙指針
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
vector<int> ret;
int i = 0, j = array.size()-1;
while(i < j){
if(array[i] + array[j] == sum){
if(ret.empty()){
ret.push_back(array[i]);
ret.push_back(array[j]);
}
else if(array[i]*array[j] < ret[0]*ret[1]){
ret[0] = array[i];
ret[1] = array[j];
}
++i;
--j;
}
else if(array[i] + array[j] < sum) ++i;
else --j;
}
return ret;
}
};
☟☟☟
(18)面試題57 - II. 和爲s的連續正數序列
輸入一個正整數 target ,輸出所有和爲 target 的連續正整數序列(至少含有兩個數)。
序列內的數字由小到大排列,不同序列按照首個數字從小到大排列。
示例 1:
輸入:target = 9
輸出:[[2,3,4],[4,5]]
示例 2:
輸入:target = 15
輸出:[[1,2,3,4,5],[4,5,6],[7,8]]
求連續數和
等差數列公式:和 = n(a1+an)/2
思路:雙指針
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
vector<vector<int>> res;
int small=1; //正整數,從1開始
int big=2;
while(small<big){
int sum=(big-small+1)*(big+small)/2; //等差數列
if(sum==target){
vector<int> tmp;
for(int i=small; i<=big; i++){ //每個數都放入
tmp.push_back(i);
}
res.push_back(tmp);
big++; //或small++;
}
else if(sum< target) big++;
else if(sum> target) small++;
}
return res;
}
};
☟☟☟
(19)面試題04. 二維數組中的查找
在一個 n * m 的二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。
示例:
現有矩陣 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
給定 target = 5,返回 true。
給定 target = 20,返回 false。
思路:定位到右上角。從右上角開始比較,比它大就往下數一行,比它小就往左數一列
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
if(matrix.size()==0 || matrix[0].size()==0) return false;//記得寫
int row=0;
int col=matrix[0].size()-1; //定位到右上角
while(row < matrix.size() && col>=0){//邊界條件
if(matrix[row][col]==target) return true;
else if(matrix[row][col] < target) row++;//如果當前位置元素比target小,則row++
else if(matrix[row][col] > target) col--;//如果當前位置元素比target大,則col--
}
return false;
}
};
☟☟☟
(20)面試題03. 數組中重複的數字
找出數組中重複的數字。
在一個長度爲 n 的數組 nums 裏的所有數字都在 0~n-1 的範圍內。數組中某些數字是重複的,但不知道有幾個數字重複了,也不知道每個數字重複了幾次。請找出數組中任意一個重複的數字。
示例 1:
輸入:
[2, 3, 1, 0, 2, 5, 3]
輸出:2 或 3
法1:用一個數組存放出現的次數
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
int n=nums.size();
vector<int> index(n,0);
int res;
for(int i=0; i<n; i++){
index[nums[i]]++;
if(index[nums[i]]>1){
res= nums[i]; //保存在全局變量,如果用局部變量,當退出for循環後,res也會消失
break;
}
}
return res;
}
};
法2:hash
其實和法1是一樣的,把hash表理解成法1的數組。
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
int n=nums.size();
map<int,int>m;
int res;
for(int i=0; i<n; i++){
m[nums[i]]++;
if(m[nums[i]]>1){
res= nums[i]; //保存在全局變量,如果用局部變量,當退出for循環後,res也會消失
break;
}
}
return res;
}
};
☟☟☟
(21)面試題39. 數組中出現次數超過一半的數字
數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。
你可以假設數組是非空的,並且給定的數組總是存在多數元素。
示例 1:
輸入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
輸出: 2
解法一:排序取中位數
//時間O(nlogn),空間O(1)
class Solution {
public:
int majorityElement(vector<int>& nums) {
sort(nums.begin(), nums.end());
return nums[nums.size()/2];
}
};
解法二:建立哈希表法
//時間O(n),空間O(n)
class Solution {
public:
int majorityElement(vector<int>& nums) {
map<int,int> m;
int res;//定義全局變量
for(int i=0; i<nums.size(); i++){
m[nums[i]]++;
if(m[nums[i]]>nums.size()/2){//不必等到哈希表完全建立再進行此判斷
res=nums[i];
}
}
return res;
}
};
解法3:摩爾投票法
核心理念爲 “正負抵消” 。時間複雜度爲 O(N) ,空間複雜度爲 O(1) 。是本題的最佳解法。
如果有符合條件的數字,則它出現的次數比其他所有數字出現的次數和還要多。
在遍歷數組時保存兩個值:一是數組中一個數字,一是次數。遍歷下一個數字時,若它與之前保存的數字相同,則次數加1,否則次數減1;若次數爲0,則保存下一個數字,並將次數置爲1。遍歷結束後,所保存的數字即爲所求。
class Solution {
public:
int majorityElement(vector<int>& numbers) {
int n = numbers.size();
if (n == 0) return 0;
int num = numbers[0], count = 1;
for (int i = 1; i < n; i++) {
if (numbers[i] == num) count++;
else count--;
if (count == 0) {
num = numbers[i];
count = 1;
}
}
return num;
}
};
☟☟☟
(22)面試題53 - I. 在排序數組中查找數字 I(統計一個數字在排序數組中出現的次數。)
統計一個數字在排序數組中出現的次數。
示例 1:
輸入: nums = [5,7,7,8,8,10], target = 8
輸出: 2
示例 2:
輸入: nums = [5,7,7,8,8,10], target = 6
輸出: 0
法1:二分查找(面試官要的)
** 二分查找,通過兩次二分查找,分別找到第一個k和最後一個k,可以使時間複雜度減少爲O(logn)**
class Solution {
public:
int search(vector<int>& nums, int target) {
int first= findFirst(nums, target);
int last=findLast(nums,target);
if(first==-1 || last==-1)
return 0;
return last-first+1;//計算次數
}
int findFirst(vector<int>& nums, int target){
int left=0, right=nums.size()-1;
while(left <= right){
int mid=left+(right-left)/2;
if(nums[mid] < target){
left++;
}
else if(nums[mid] > target){
right--;
}
else if(mid-1>=0 && nums[mid-1]==target){
right=mid-1;
}
else
return mid;
}
return -1;
}
int findLast(vector<int>& nums, int target){
int left=0, right=nums.size()-1;
while(left <= right){
int mid=left+(right-left)/2;
if(nums[mid] < target){
left++;
}
else if(nums[mid] > target){
right--;
}
else if(mid+1<nums.size() && nums[mid+1]==target){
left=mid+1;
}
else
return mid;
}
return -1;
}
};
其他:簡單暴力,不是面試官想要的;
法2:
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
return count(data.begin(),data.end(),k);
}
};
法3:
//利用C++ stl的二分查找
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
auto resultPair = equal_range(data.begin(), data.end(),k);
return resultPair.second - resultPair.first;
}
};
法4:
class Solution {
public:
int GetNumberOfK(vector<int> data ,int k) {
int num=0;
for(int i=0;i<data.size();i++){
if(data[i]==k)
num++;
}
return num;
}
};
☟☟☟
(23)面試題53 - II. 0~n-1中缺失的數字
一個長度爲n-1的遞增排序數組中的所有數字都是唯一的,並且每個數字都在範圍0~n-1之內。在範圍0~n-1內的n個數字中有且只有一個數字不在該數組中,請找出這個數字。
示例 1:
輸入: [0,1,3]
輸出: 2
示例 2:
輸入: [0,1,2,3,4,5,6,7,9]
輸出: 8
二分!!! 排序數組中的搜索問題,首先想到 二分法 解決。
根據題意,數組可以按照以下規則劃分爲兩部分。
左子數組: nums[i]=i ;
右子數組: nums[i]!=i ;
缺失的數字等於 “右子數組的首位元素” 對應的索引;因此考慮使用二分法查找 “右子數組的首位元素” 。
class Solution {
public:
//二分
int missingNumber(vector<int>& nums) {
int left=0;
int right=nums.size()-1;
while(left<=right){
int mid=left+(right-left)/2;
if(nums[mid]==mid){
left=mid+1;
}
else // !=
right=mid-1;
}
return left;
}
};
☟☟☟
(24)面試題66:構建乘積數組
給定一個數組 A[0,1,…,n-1],請構建一個數組 B[0,1,…,n-1],其中 B 中的元素 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。
示例:
輸入: [1,2,3,4,5]
輸出: [120,60,40,30,24]
思路:
B[i]的值可以看作下圖的矩陣中每行的乘積。
下三角用連乘可以很容求得,上三角,從下向上也是連乘。
因此我們的思路就很清晰了,先算下三角中的連乘,即我們先算出B[i]中的一部分,然後倒過來按上三角中的分佈規律,把另一部分也乘進去。
class Solution {
public:
vector<int> constructArr(vector<int>& a) {
int n=a.size();
vector<int> b(n);
if(n!=0){
b[0]=1; //計算下三角連乘
for(int i=1; i<n; i++){
b[i]=b[i-1]*a[i-1];
}
//計算上三角
int tmp=1;
for(int j=n-2; j>=0; j--){
tmp=tmp*a[j+1];
b[j]=b[j]*tmp;
}
}
return b;//從B[0]~B[n-1]
}
};
☟☟☟
(25)面試題21. 調整數組順序使奇數位於偶數前面
輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得所有奇數位於數組的前半部分,所有偶數位於數組的後半部分。
示例:
輸入:nums = [1,2,3,4]
輸出:[1,3,2,4]
注:[3,1,2,4] 也是正確的答案之一。
法1思路:首尾雙指針
定義頭指針 left ,尾指針 right .
left 一直往右移,直到它指向的值爲偶數
right 一直往左移, 直到它指向的值爲奇數
交換 nums[left] 和 nums[right] .
重複上述操作,直到 left == rightleft==right .
class Solution {
public:
vector<int> exchange(vector<int>& nums) {
// if (nums.empty()) return nums;
int l = 0, r = nums.size() - 1;
while (l < r) {
while (l < r && nums[l] % 2 == 1) ++l;
while (l < r && nums[r] % 2 == 0) --r;
swap(nums[l], nums[r]);
}
return nums;
}
};
法2: 用一個新數組保存 low
class Solution {
public: //評論裏說此方法有點low!
void reOrderArray(vector<int> &array) {
vector<int> res;
for(int i = 0; i < array.size(); i++)
{
if(array[i] % 2 == 1)
res.push_back(array[i]);
}
for(int i = 0; i < array.size(); i++)
{
if(array[i] % 2 == 0)
res.push_back(array[i]);
}
//array.swap(res);
array = res;
}
};
法3(沒看):插入算法思想,先把當前的數保存,整體後移。。
class Solution {
public:
//插入排序思想
void reOrderArray(vector<int> &array) {
int size=array.size();
for(int i=0;i<size;i++){
int target = array[i];
if(array[i] % 2 == 1){
int j = i;
while(j >= 1 && array[j-1] % 2 == 0){
array[j] = array[j-1];
j--;
}
array[j] = target;
}
}
}
};
☟☟☟
(26)面試題61. 撲克牌中的順子
從撲克牌中隨機抽5張牌,判斷是不是一個順子,即這5張牌是不是連續的。2~10爲數字本身,A爲1,J爲11,Q爲12,K爲13,而大、小王爲 0 ,可以看成任意數字。A 不能視爲 14。
示例 1:
輸入: [1,2,3,4,5]
輸出: True
示例 2:
輸入: [0,0,1,2,5]
輸出: True
思路:排序後,求出0的數量,求出空的間隔數,比較它們
class Solution {
public:
bool isStraight(vector<int>& nums) {
int size=nums.size();
//if(size<=0) return false;//沒有撲克
sort(nums.begin(), nums.end());//注意排序
int numof0=0;//0的數量
for(int i=0; i<size; i++){
if(nums[i]==0) numof0++;
}
int kong=0;//空格數量
for(int i=numof0+1; i<size; i++){
if(nums[i]==nums[i-1]) return false;
else kong += nums[i]-nums[i-1]-1;
}
if(numof0 >= kong) return true;
else return false;
}
};
知識點:sort
c++中的sort函數和實例
sort中的比較函數compare要聲明爲靜態成員函數或全局函數,不能作爲普通成員函數,否則會報錯。 因爲:非靜態成員函數是依賴於具體對象的,而std::sort這類函數是全局的,因此無法再sort中調用非靜態成員函數。靜態成員函數或者全局函數是不依賴於具體對象的, 可以獨立訪問,無須創建任何對象實例就可以訪問。同時靜態成員函數不可以調用類的非靜態成員。
通俗說明用法:
c++中的sort函數一般用來對數組進行排序,有三個參數,第一個參數是是數組的起始位子,第二個參數爲你要排序的數組的終止位子。
第三個參數一般是排序的條件,可以通過這個參數達到各種各樣的排序(後面再講),也可以不寫,默認是升序。
如:int arr[5]={1,3,2,5,4}. 操作:sort(arr,arr+5). 結果{1,2,3,4,5} //默認升序
如: int arr[5]={1,3,2,5,3}. 操作:sort(arr,arr+3) 結果{1,2,3,5,4} //對數組可以部分操作
這裏我對第三個參數進行詳細解釋:第三個參數可以是一個函數,如果該函數返回爲真,就將操作對象位子不變,否則交換位子(後面有例子)。
我們可以通過調整該函數的內容來控制,當某個條件滿足時返回值的真假。
如:例如一個數組{32,3}這兩個數如何拼接組合達到的數最小,兩種情況323,332。顯然323小。這類問題可以用sort來進行操作。
代碼如下:
int arr[3]={3,32,321} // 組成最小數是321323
sort(arr,arr+3,cmp). //對數組三個位子進行操作,條件是cmp函數,一般是bool類型函數
static bool cmp(int a, int b)
{
string A = to_string(a)+to_string(b);
string B =to_string(b)+to_string(a);
return A<B;
}
//函數的意思是sort函數操作的對象數組中兩個挨着的順序的元素,分別賦值到a和b上。
通過一系列操作,滿足條件某個條件(題中條件是A<B),返回如果真,兩個數順序不變,如果返回假,兩個元素交換位子。
詳細解讀上面得到最小值321323的過程:
從sort函數開始,將數組前兩個值3,32丟入cmp中。即a=3,b=32.先將a和b轉換成字符串
再拼接故A=“332” B=“323” 故return A<B這個條件是假值,sort函數將這兩個值的位子交換。此時arr={32,3,321}.
接下來類似操作依次得到結果是:
arr={32,321,3}
arr={321,32,3}。
將上述數組遍歷輸出及得到結果。(到這裏sort內容講完)
☟☟☟
(27)面試題45. 把數組排成最小的數
輸入一個正整數數組,把數組裏所有數字拼接起來排成一個數,打印能拼接出的所有數字中最小的一個。
示例 1:
輸入: [10,2]
輸出: "102"
示例 2:
輸入: [3,30,34,5,9]
輸出: "3033459"
思路:sort(第三個參數排序)
class Solution {
public:
string minNumber(vector<int>& nums) {
int len=nums.size();
if(len==0) return "";
string res;
sort(nums.begin(), nums.end(), cmp);
for(int i=0; i<len; i++){
res += to_string(nums[i]); //所以這裏還得to_string
}
return res;
}
//是的,必須 static
static bool cmp(int a, int b){//只是交換了原數組中的位置,並沒有把它們變成string
string A=to_string(a)+to_string(b);
string B=to_string(b)+to_string(a);
return A<B;
}
};
☟☟☟
(28)劍指11.旋轉數組的最小數字(189. Rotate Array)【該題歸類到了查找與排序(二分)】
把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。輸入一個遞增排序的數組的一個旋轉,輸出旋轉數組的最小元素。例如,數組 [3,4,5,1,2] 爲 [1,2,3,4,5] 的一個旋轉,該數組的最小值爲1。
示例 1:
輸入:[3,4,5,1,2]
輸出:1
示例 2:
輸入:[2,2,2,0,1]
輸出:0
思路:二分查找
該題減治的思想是:通過排除不可能是最小值元素,進而縮小範圍。
當我們拿中間的數和最右邊的數相比時,有三種情況:
1. 中間的數比右邊的大,那麼中間數不可能是最小的數,
最小的數只可能出現在中間數的後面,改left = mid + 1縮小區間
3. 中間的數和右邊的小,那麼右邊的數不可能是中位數,
此時,中間的數可能是最小的數,改right = mid 縮小區間
5. 中間的數和右邊相等,例如[3,3,3,1,3]此時中間的數和最右邊的數都爲3,
可以知道的是,此時我們可以排除最右邊的數,改區間爲right = right - 1
class Solution {
public:
int minArray(vector<int>& numbers) {
int i=0, j=numbers.size()-1;
while(i < j){ //當i=j時,跳出循環,返回i所指元素
int m=i + (j - i) / 2;//int m=(i+j)/2;
if(numbers[m]>numbers[j]) i=m+1;
else if(numbers[m] < numbers[j]) j=m;
else j--;
}
return numbers[i];
}
};
while (i < j) 循環裏應該是<=吧。請問爲什麼是<
☟☟☟
(29)面試題51. 數組中的逆序對【困難】
在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數。
示例 1:
輸入: [7,5,6,4]
輸出: 5
思路:歸併排序
貌似是劍指最難的一道,看了半天放棄了。這裏先貼一個可通過的代碼。複習了排序算法再返回來看。
class Solution {
public:
int InversePairs(vector<int> data) {
int length=data.size();
if(length<=0)
return 0;
//vector<int> copy=new vector<int>[length];
vector<int> copy;
for(int i=0;i<length;i++)
copy.push_back(data[i]);
long long count=InversePairsCore(data,copy,0,length-1);
//delete[]copy;
return count%1000000007;
}
long long InversePairsCore(vector<int> &data,vector<int> ©,int start,int end)
{
if(start==end)
{
copy[start]=data[start];
return 0;
}
int length=(end-start)/2;
long long left=InversePairsCore(copy,data,start,start+length);
long long right=InversePairsCore(copy,data,start+length+1,end);
int i=start+length;
int j=end;
int indexcopy=end;
long long count=0;
while(i>=start&&j>=start+length+1)
{
if(data[i]>data[j])
{
copy[indexcopy--]=data[i--];
count=count+j-start-length; //count=count+j-(start+length+1)+1;
}
else
{
copy[indexcopy--]=data[j--];
}
}
for(;i>=start;i--)
copy[indexcopy--]=data[i];
for(;j>=start+length+1;j--)
copy[indexcopy--]=data[j];
return left+right+count;
}
};