1. 程式人生 > 其它 >演算法筆記(沒寫完)

演算法筆記(沒寫完)

關於指標的操作

int x;
int * p1 = &x; // 指標可以被修改,值也可以被修改
const int * p2 = &x; // 指標可以被修改,值不可以被修改(const int)
int * const p3 = &x; // 指標不可以被修改(* const),值可以被修改
const int * const p4 = &x; // 指標不可以被修改,值也不可以被修改
//指標函式:返回一個型別是指標的函式
int* func(int a,int b){
     int* sum=new int(a+b);
     return sum; 
}

//函式指標:指向函式的指標
int subtraction(int a, int b) { return a - b; } //minus就是函式指標 int (*minus)(int,int)=subtraction; int operation(int x, int y, int (*func)(int, int)) { return (*func)(x,y); }

167、

給你一個下標從 1 開始的整數陣列numbers ,該陣列已按 非遞減順序排列 ,請你從陣列中找出滿足相加之和等於目標數target 的兩個數。如果設這兩個數分別是 numbers[index1] 和 numbers[index2] ,則 1 <= index1 < index2 <= numbers.length 。

以長度為 2 的整數陣列 [index1, index2] 的形式返回這兩個整數的下標 index1 和 index2。

你可以假設每個輸入 只對應唯一的答案 ,而且你 不可以 重複使用相同的元素。

你所設計的解決方案必須只使用常量級的額外空間。

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int index1=0;
        int index2=numbers.size()-1;
        while(index1<index2)
        {
                
if(numbers[index1]+numbers[index2]>target) index2--; if(numbers[index1]+numbers[index2]<target) index1++; if(numbers[index1]+numbers[index2]==target) return {index1+1,index2+1}; } return {-1,-1}; } };

88、

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

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

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

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int p1 = 0, p2 = 0;
        int sorted[m + n];
        int cur;
        while (p1 < m || p2 < n) {
            if (p1 == m) {
                cur = nums2[p2++];
            } else if (p2 == n) {
                cur = nums1[p1++];
            } else if (nums1[p1] < nums2[p2]) {
                cur = nums1[p1++];
            } else {
                cur = nums2[p2++];
            }
            sorted[p1 + p2 - 1] = cur;
        }
        for (int i = 0; i != m + n; ++i) {
            nums1[i] = sorted[i];
        }
    }
};

142、

給定一個連結串列的頭節點 head,返回連結串列開始入環的第一個節點。如果連結串列無環,則返回null。

如果連結串列中有某個節點,可以通過連續跟蹤 next 指標再次到達,則連結串列中存在環。 為了表示給定連結串列中的環,評測系統內部使用整數 pos 來表示連結串列尾連線到連結串列中的位置(索引從 0 開始)。如果 pos 是 -1,則在該連結串列中沒有環。注意:pos 不作為引數進行傳遞,僅僅是為了標識連結串列的實際情況。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* m=head,*k=head;
        while(k!=NULL&&k->next!=NULL)
        {
            k=k->next->next;
            m=m->next;
            if(k==m) 
            {
                k=head;
                while(k!=m)
                {
                    k=k->next;
                    m=m->next;
                }
                return k;
            }
        }
        return NULL;
    }
};

第一次出錯:對快慢表理解不正確,當第一次相遇時只能確定存在環區,第二次相遇才是才是環的起點

第二次出錯:由於快指標一次走兩個,無環情況快指標可能會訪問到空指標出現錯誤(還有幾次也是這個錯誤,要細心考慮不能太急躁了)

76、

給你一個字串s、一個字串t。返回s中涵蓋t所有字元的最小子串。如果s中不存在涵蓋t所有字元的子串,則返回空字串""

class Solution {
public:
//檢查hs中是否包含了ht的全部字元以及個數
  bool check(unordered_map<char, int> &hs, unordered_map<char, int> &ht){
    for(unordered_map<char,int>::iterator it=ht.begin();it!=ht.end();it++){
      if(hs[it->first]<it->second){
        return false;
      }
    }
    return true;
  }
  string minWindow(string s, string t) {
    unordered_map<char, int> hs; //用於儲存字串s的滑動窗口裡元素出現次數
    unordered_map<char, int> ht; //儲存字串t中各元素出現次數

    int minlen = INT_MAX; //記錄最小長度
    int ansL = -1; //子串的左邊起點

    for(int i=0;i<t.size();i++) ht[t[i]]++; //遍歷t,並統計各字元出現次數

    //滑動視窗,讓k不斷往右移動,找到一個滿足條件的子串
    for(int i=0,j=0;i<=j&&j<s.size();){
    //如果滑動視窗內元素是空的並且j所指元素不在ht中,可以讓i和j都先右移,直到碰見第一個元素在ht裡面
      if(hs.empty()&&ht.find(s[j])==ht.end()){
        i++;
        j++;
      }
    //如果j處元素在ht中有,那麼j處元素也需要儲存到hs中
    if(ht.find(s[j])!=ht.end()) hs[s[j]]++; //往hs裡面填充有效元素,非必要的不要放進去

    //如果此時hs已經包含了ht中全部字元,就需要讓i往右移
    while(check(hs,ht)&&i<=j){
      //如果當前子串長度更小,則更新
      if(j-i+1<minlen){
        minlen = j-i+1;
        ansL = i; //記錄起點
      }
      //如果i處元素在ht中有,將出現hs中對應出現次數-1
      if(ht.find(s[i])!=ht.end()) hs[s[i]]--; //元素出現次數減1
      i++; //i往右移
    }
    j++; //j繼續右移
  }
  if(ansL==-1) return "";
  else{
    return s.substr(ansL, minlen);
    }
  }
};

可以想到利用滑動視窗確定範圍,但不知道怎麼確定範圍中的字元是否存在。

此程式碼和以下解析來源於作者:traveller-lzx。

利用滑動視窗的思想,不斷更新視窗的左邊界和右邊界;
利用雜湊表ht儲存t中所有的元素和對應出現次數,雜湊表hs儲存滑動視窗中出現的有效元素(就是該元素在t/ht中出現過的元素)及對應出現次數;
讓視窗的右邊界j不斷右移,直到視窗內的有效元素hs集合中元素出現次數>=ht中相應元素出現次數,此時讓視窗左邊界i開始向右縮小,並逐個剔除元素,直到視窗不能再縮小為止,儲存此時視窗內有效子串的長度為minlen,並記錄該子串的起點為ansL;
迴圈迭代上述過程,最後利用substr()函式,返回子串即可;
優化思路:

此程式碼基本按照官方的題解思路進行編寫,有以下幾個細節需要注意:
剛開始當還沒有建立滑動視窗時,可能j指標會指向很多無效元素,例如"s=xxxABCxx,t=ABC",我們可以讓i和j一開始先跳過前面的xxx,具體看程式碼實現;
雜湊表hs需要存放有效元素(就是該元素在t/ht中出現過的元素),而不是每遍歷一個元素,都要存進去,這樣會浪費記憶體,最後不一定能通過;
check()函式裡傳遞引數用引用傳遞的方式,如果是拷貝構造,可能會因為資料量過大,最後一個測試用例過不了;
更新res子串並不需要在調整視窗左邊界的過程中進行,而是用子串長度minlen和子串起始位置ansL來記錄即可,更新記錄,最後再用substr()返回;
程式碼

作者:traveller-lzx
連結:https://leetcode-cn.com/problems/minimum-window-substring/solution/zui-xiao-fu-gai-zi-chuan-hua-dong-chuang-fado/
來源:力扣(LeetCode)