【C++刷题】习题集

一、整数

1.多数元素

  • 思想:两两pk,最后留下的就是最多次出现的数。
  • 本质上利用的是众数的性质。
class Solution {
public:
    int majorityElement(vector<int>& nums) 
    {
        //候选人算法——根据众数进行pk
        int candidate = nums[0];
        int get_count = 0;
        for(auto e : nums)
        {
            if(candidate == e)
                get_count++;
            else
                get_count--;
            
            if(get_count == 0)
            {
                candidate = e;
                get_count = 1;
            }
        }
        return candidate;
    }
};

2.异或实现加法

  • 思想:二进制加法原理,两数之和可以分为两部分,两数按位与再左移一位得到进位信息,异或得到的是不带进位信息的结果。结束条件为没有进位信息。
class Solution {
public:
    int Add(int num1, int num2) 
    {
        int begin = num1;
        int add = num2;
        int end;
        while(add)
        {
            end = begin ^ add;
            add = ((begin & add)<<1);
            begin = end;
        }
        return begin;
    }
};

3.奇偶数交换

  • 思想:奇数位全是1的数:0xaaaaaaaa 偶数位全是1的数:0x55555555,按位与再异或操作即可换位。
class Solution {
public:
    int exchangeBits(int num) 
    {
        int Odd = num & 0xaaaaaaaa;//奇数位
        int Even = num & 0x55555555;//偶数位

        int res = (Odd>>1) ^ (Even << 1); 

        return res;
    }
};

4.阶乘尾数

  • 观察规律:5 ,10,15 ,20,最多各自产生一个0,25与4结合能产生两个0,125与8结合能产生三个0,也就是说,只含有单个5因子的数,能产生1个0,含有两个5的因子的数能产生两个0,以此类推产生n个5的因子,能产生n个0。算的话我们可以累加计算,也就是说每多一个5因子多产生一个0,那么只需要不断除以5的倍数,再加上结果即可。
class Solution {
public:
    int trailingZeroes(int n) 
    {

        int sum = 0;
        long long k = 5;
        while(1)
        {
            if(n / k)
                sum += n/k;
            else
                break;
            k*=5;
        }
        return sum;
    }
};

二、哈希

1.找到所有数组中消失的数字

  • 思想:根据数据范围通过加密原数组,来得到信息——是否存在。再通过遍历解密,得到未被加密的数组的下标即为消失的数字。
class Solution {
public:
    vector<int> findDisappearedNumbers(vector<int>& nums) 
    {

        vector<int> ret;
        int n = nums.size();
        for (auto e : nums) 
        {
            int x = e % (n + 1);
            nums[x-1] += ( n + 1);
        }
        for(int i = 0 ; i < n; i++)
        {
            if(nums[i] <= n)
                ret.push_back(i+1);
        }

        return ret;
    }
};

2.珠玑妙算

  • 思想:哈希,先算真猜中的次数,剩余记录槽位颜色出现的次数,在用剩下的猜的槽位颜色看是否在结果的槽位上有,有就加上两者最小槽位的出现次数。
class Solution {
public:
    vector<int> masterMind(string solution, string guess) 
    {
        vector<int> ret(2);
        map<char, int> sol;
        map<char, int> gue;

        //先考虑是否真猜中了
        for(int i = 0; i < 4; i++)
        {
            if(solution[i] == guess[i])
            {
                ret[0]++;
            }
            else
            {
                //没猜中,记录猜的槽位颜色出现的次数,用于判断是否伪猜中。
                sol[solution[i]]++;
                gue[guess[i]]++;
            }
        }
        for(auto e : gue)
        {
            //gue的槽位颜色在sol出现,即为伪猜中。
            if(sol[e.first] != 0)
            {
                //伪猜中的肯定是最小次数。
                ret[1] += min(sol[e.first],e.second);
            }
        }
        return ret;
    }
};

3.数列

  • 思想:对于这种不断输入不断再求的题,我们可以一次性求完,然后再一次性输出,这样复杂度会降低不少。
#include <iostream>
#include <vector>
#define MAX 1000000

using namespace std;

int main() 
{

    vector<int> dp(MAX + 1);
    dp[1] = 1,dp[2] = 2;

    for(int i = 3; i <= MAX; i++)
    {
        dp[i] = 2 * dp[i-1] + dp[i-2];
        dp[i] %= 32767;
    }

    int n;
    cin >> n;
    while(n--)
    {
        int k;
        cin>>k;
        cout << dp[k]<<endl;
    }
    return 0;
}

4.最长和谐子序列

  • 思想:哈希,key(值)—— val(值出现的次数),目标找val(最小值)和val+1(最大值)的下标,相加得到长度,因为数组是可以进行切割的,因此只需在哈希表中找到最长的(val和val+1)出现的次数即可。
lass Solution {
public:
    int findLHS(vector<int>& nums) 
    {
        unordered_map<int,int> last;
        int n = nums.size();
        for(int i = 0; i < n; i++)
        {
            last[nums[i]]++; 
        }
        int len = 0;
        for(auto e : nums)
        {
            if(last[e] && last[e+1])
            {
                int n = last[e] + last[e+1];
                len = len > n ? len : n;
            }
        }

        return len;
    }
};

5.快照数组

  • 思想:哈希记录index索引信息,map记录最新修改的val对应的快照编号,这样降低了内存的消耗。若用快照编号找,得找到比当前快照编号一个大的,前面小于等于此快照编号的就是对应的val。
class SnapshotArray {
public:
    int _id = 0;
    unordered_map<int,map<int,int>> arr;
    //index, _id, val。索引值是递增的,因此用map排序较为合适
    SnapshotArray(int length) {}
    
    void set(int index, int val) 
    {
        arr[index][_id] = val;
        //记录索引值对应快照号的值。
    }
    
    int snap() 
    {
        return _id++;
    }
    
    int get(int index, int snap_id) 
    {
        //先看index在arr中存不存在
        auto is_find = arr.find(index);
        if(is_find == arr.end())
            return 0; //不存在说明为初始值——0
        //如果snap_id对应的数组的值没有被修改,就还是以前快照的snap_id
        //修改了就是当前的snap_id
        //因此找到比snap_id大的前一个即可
        auto it = arr[index].upper_bound(snap_id);
        //如果it等于begin说明前面没有小于等于snap_id的
        return it == arr[index].begin() ? 0 : (--it)->second;
    }
};

6.LRU缓存

  • 思想 :读题不难看出是哈希,但还得借助某种数据结构,这里采用的是插入删除,因此是链表比较合适,又因为get和put都要随机读取位置,因此要哈希存位置。剩下的根据题目要求慢慢来不及,说明一点,自己实现的双向链表会更加符合要求,面试官一般都考实现。

偷个懒,直接用库里的了:

class LRUCache {
public:
    LRUCache(int capacity) 
    {
        _capacity = capacity;
        _sz = 0;
    }
    
    int get(int key) 
    {
        //存在缓存的条件是:1.key能在哈希表中找到。
        auto it = kv.find(key);
        if(it != kv.end())
        {
            //因为要从缓存中获取所以要更新key所在缓存k的位置。
            auto del = ki[key];
            k.push_back(key);
            k.erase(del);
            auto last = --k.end();
            ki[key] = last;
            return it->second;
        }
        return -1;
    }
    
    void put(int key, int value) 
    {

        //如果不存在
        auto is_find = kv.find(key);
        if(is_find == kv.end())
        {
            //看k的容量是否满了。
            if(_sz == _capacity)
            {
                _sz--;
                //把哈希表存位置的信息删除
                auto del_1 = k.begin();
                int key = *del_1;
                ki.erase(key);
                //把这个结点删除了。
                k.erase(del_1);
                //把kv的信息删除了
                auto del_2 = kv.find(key);
                kv.erase(del_2);
            }

            //在缓存中添上这个值
            k.push_back(key);
            //在存放缓存的信息的表中存放这个值
            auto last = --k.end();
            ki[key] = last;
            _sz++;
        }
        else
        {
            auto it = kv.find(key);
            if(it != kv.end())
            {
                //因为要从缓存中获取所以要更新key所在缓存k的位置。
                auto del = ki[key];
                k.push_back(key);
                k.erase(del);
                auto last = --k.end();
                ki[key] = last;
            }
        }
        
        kv[key] = value;
        
    }
    list<int> k;
    unordered_map<int,int> kv;
    unordered_map<int,list<int>::iterator> ki;
    int _sz,_capacity;
};

三、二分

1.寻找峰值

  • 思想:二分,因为根据mid的状态可以切分数据,如果mid是 / 的趋势,那峰顶就在右边,如果 \的趋势,那就在左边,如果是^的趋势,那就到峰顶了。
#include <vector>
class Solution {
public:
    bool judge(vector<int>& nums, int x)
    {
        int n = nums.size();

        //处理特殊情况
        if(n == 1)
            return true;
        //处理边界情况
        if(x == 0)
            return nums[1] < nums[0];
        if(x == n-1)
            return nums[n-2] < nums[n-1];
        
        //正常判断
        if(nums[x] > nums[x-1] && nums[x] > nums[x+1])
            return true;
        else
            return false;

    }
    bool is_up(vector<int>& nums, int x)
    {
        //x值处于上升趋势,严格递增。
        if(nums[x] > nums[x-1] && nums[x] < nums[x+1])
            return true;
        else
            return false;
    }
    int findPeakElement(vector<int>& nums) 
    {
        int left = 0,right = nums.size() - 1,mid = (right + left) / 2;
        while(left <= right)
        {
            if(judge(nums,mid))
                return mid;
            else
            {
                if(is_up(nums,mid))
                    left = mid + 1;
                else
                    right = mid - 1;
            }
            mid = (right + left) / 2;
        }
        //如果找不到
        return -1;
    }
};

2.适龄的朋友

  • 思想:二分+排序+哈希。排的是升序,哈希记录的是值出现的次数,二分根据条件可进行分割数据,相同年龄的好友可互发信息。
class Solution 
{
public:
    void judge(vector<int>& ages,unordered_map<int,int>& count,\
    int& cnt)
    {
        int sz = ages.size();
        count[ages[0]]++;
        for(int i = 1; i < sz; i++)
        {
            count[ages[i]]++;
            //二分查找
            int left = 0,right = i - 1;
            while(left < right)
            {
                int mid = (left + right) / 2;

                if(ages[mid] * 2 > ages[i] + 14)
                {
                    //发送请求[mid,right]是适龄好友
                    right = mid;
                }
                else
                {
                    //不能发送请求[left,mid]不是适龄好友
                    left = mid + 1;
                }
            }

            //[left,right]区间可能都不是适龄好友。
            //所以只需判断left或者right(left == right)是否是适龄好友即可
            if(ages[left] * 2 > ages[i] + 14)
            {
                //是适龄好友,那么就加上区间[left,i)的个数即可。
                cnt += (i - left); 
                //相等是一种特殊的情况,可以互发因此多发一次。
                cnt += (count[ages[i]] - 1);
            }
        }

    }

    int numFriendRequests(vector<int>& ages) 
    {

        vector<int> youngth;
        unordered_map<int,int> count;

        //进行排升序。
        sort(ages.begin(),ages.end());

        int cnt = 0;
        judge(ages,count,cnt);

        return cnt;
    }
};

3.搜索插入位置

  • 思路:二分,根据target来切分数据,进而不断逼近,注意判断条件即可。
class Solution {
public:
    int searchInsert(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
                left = mid + 1;
        }

        return left;    
    }
};

4.搜索旋转排序数组

  • 思想:二分,根据mid可得到一段升序区间,根据target是否在这段区间,来切割数据段或者直接用二分查找。
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] >= nums[left])
            {
                //左区间是顺序区间
                
                //判断target是否在顺序区间。
                (target >= nums[left] && target < nums[mid]) ? \
                right = mid - 1:\
                 left = mid + 1; 
            }
            else 
            {
                //右区间是顺序区间
                (target <= nums[right] && target > nums[mid]) ? \
                left = mid + 1 :\
                right = mid - 1;
            }

            if(nums[mid] == target)
                return mid;
        }
        return -1;
    }
};

四、前缀

1.除自身以外数组的乘积

  • 思想:前缀+后缀。前缀求左边乘积,后缀求右边乘积,除自身以外的等于,前缀乘以后缀。
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) 
    {
        int n = nums.size();

        //多开辟一个元素,存右侧乘积
        vector<int> ret(n+1);
        //因为0下标没有左侧元素,因此设为1
        ret[0] = 1;
        for(int i = 1; i < n; i++)
        {
            ret[i] = ret[i-1] * nums[i-1];
        }

        //右侧元素的乘积,顺便求一下所求。
        //因为n-1右侧没有元素,因此设为1
        ret[n] = 1;
        for(int i = n - 1; i >= 0; i--)
        {
            //所求等于左侧元素乘以右侧元素。
            ret[i] = ret[i]*ret[n];

            ret[n]*= nums[i];
        }
        ret.resize(n);
        return ret;
    }
};

五、双指针

1.按奇偶排序数组

  • 思想:一个指针维护奇数位下标的数组,一个指针维护偶数下标位的数组。
class Solution {
public:
    vector<int> sortArrayByParityII(vector<int>& nums) 
    {
        //双指针算法
        int i = 0 , j = 1;
        //i维护偶数区间,j维护奇数区间


        int sz = nums.size();
        cout << i << endl;
        for( ; i < sz; i += 2)
        {
            if(nums[i] % 2 != 0)
            {
                while(nums[j] % 2 != 0)
                    j += 2;

                    swap(nums[i],nums[j]);                
            }
        }
        return nums;
    }
};

2.两数之和

  • 思想:双指针,不过细节在于数据可能会相等,因此得用multimap。
#include <vector>
#include<algorithm>
class Solution {
public:

    vector<int> twoSum(vector<int>& numbers, int target) 
    {
        multimap<int,int> m;
        int n = numbers.size();
        for(int i = 0 ; i < n; i++)
        {
            m.insert(make_pair(numbers[i],i));
        }
        
        auto begin = m.begin();
        auto end = --m.end();

        while(true)
        {
            
            if(begin->first + end->first >target)
                end--;
            else if(begin->first + end->first < target)
                begin++;
            else
                break;
        }
        vector<int> v(2);
        v[0] = begin->second + 1;
        v[1] = end->second + 1;
        if(v[0] > v[1])
            swap(v[0],v[1]);
        return v;
    }
};

六、字符串

1.整理字符串

  • 思想:采用栈的数据结构,这一点有点难看出,遇到相减的绝对值等于32的就出栈,否则就入栈,最后将栈转化为字符串,或者直接用字符串当做栈。
class Solution {
public:
    string makeGood(string s) 
    {
        stack<char> st;
        st.push(s[0]);
        int n = s.size();
        for(int i = 1; i < n; i++)
        {
            if(!st.empty() && (abs(st.top() - s[i]) == 32))
                st.pop();
            else
                st.push(s[i]);
        }

        string ret;
        while(!st.empty())
        {
            ret+=st.top();
            st.pop();
        }
        reverse(ret.begin(),ret.end());
        return ret;
    }
};

2.字符串相乘

  • 思路:类似于矩阵的错位乘,固定好一个数,然后让一个数的每一位乘以另外一个数的每一位,每乘一位进行错位,将计算结果进行%10之后加在对应位置的数组中,进位保存,之后最后看是否还有进位,再加上即可,最后将存好的信息的数组转换为字符串即可。
class Solution {
public:
    string multiply(string num1, string num2) 
    {

        if(num1 == "0" || num2 == "0")
            return "0";
        int len1 = num1.size();
        int len2 = num2.size();

        vector<short> ret(len1 + len2,0);

        int mod = 0;
        for(int i = len1 - 1; i > -1; i--)
        {
            //[len2,len1 + len2 - 1]
            int pos = len2 + i;
            int n1 = num1[i] - '0';
            if(n1 || mod)
            {
                if(n1)
                {
                    for(int j = len2 - 1; j > -1; j--)
                    {   
                        int n2 = num2[j] - '0';
                        if(n2 || mod)
                        {
                            int product = n1 * n2 + mod; 

                            int add = product % 10;
                            mod = product / 10;

                            ret[pos] += add;
                            mod += ret[pos] / 10;
                            ret[pos] %= 10;   
                        }
                        pos--;
                    }
                }
                if(mod)
                {
                    ret[pos] += mod;
                    mod = ret[pos] / 10;
                    ret[pos] %= 10;
                }
            }
        }
        string product;
        int i = ret[i] == 0 ? 1 : 0;
        for( ; i < len1 + len2; i++)
        {
            product += to_string(ret[i]);
        }


        return product;
    }
};
  • 思想:一般的加法思路,先计算每一个数相乘之后的结果,然后补上错位的后缀0,最后再用字符串将每一位数进行相加即可。
class Solution {
public:
    string multiply(string num1, string num2) 
    {
        //串行乘法器的实现
        //先实现每个数的与乘数的乘积
        //补上后缀0.
        //再实现加法运算。

        vector<string> num;

        int sz = num2.size();
        int zero = 0;
        for(int i = sz - 1; i > -1; i--)
        {
            if(num2[i] == '0')
            {
                num.push_back("0");
                zero++;
            }
            else
            {
                string str;
                int mod = 0;
                int sz = num1.size();
                for(int j = sz - 1; j > -1; j--)
                {
                        int n1 = num1[j] - '0';
                        int n2 = num2[i] - '0';
                        int add = (n1 * n2 + mod) % 10;
                        mod = (n1 * n2 + mod) / 10;
                        str += to_string(add);
                }

                //最后看有没有余数
                if(mod)
                    str += to_string(mod);

                reverse(str.begin(),str.end());
                //再加后缀0
                int tmp = zero;
                while(tmp != 0)
                {
                    str += to_string(0);
                    --tmp;
                }
                zero++;
                //最后放入vector。
                if(str[0] != '0')
                    num.push_back(str);
                else
                    num.push_back("0");
            }
        }


        //将字符串进行相加。
        sz = num.size();
        string sum = num[0];
        for(int i = 1; i < sz; i++)
        {
            string n1 = num[i];
            int sz1 = n1.size();
            int sz2 = sum.size();
            int mod = 0;
            string ret;
            while(sz1 || sz2)
            {
                int add1 = sz1 == 0 ? 0 : n1[sz1-1] - '0';
                int add2 = sz2 == 0 ? 0 : sum[sz2 -1] - '0';

                int end = (add1 + add2 + mod) % 10;
                mod = (add1 + add2 + mod) / 10;

                ret += to_string(end);
                
                if(sz1)
                    sz1--;
                if(sz2)
                    sz2--;
            }

            if(mod)
                ret+=to_string(mod);
            
            reverse(ret.begin(),ret.end());
            sum = ret;
        }

        return sum;
        
    }
};

七、链表与二叉树

1.从链表中删去总和值为零的连续结点

  • 思想:哈希+前缀和,哈希记录的是前缀和相等的最后一次出现的位置。也就是题目中的总和值为0的区间的最后一个元素的位置,然后再根据前缀和跳到前缀和相等的最后一个元素的位置,或者说总和值为0的区间的最后一个位置,结点指向其下一个结点即可。
class Solution {
public:
    ListNode* removeZeroSumSublists(ListNode* head) 
    {
        ListNode* newhead = new ListNode(0);
        unordered_map<int,ListNode*> pos;
        newhead->next = head;
        int sum = 0;

        //思路:
        //前缀和相等——说明之间的区间相加等于0。范围是( ]
        //为了跳过这一段区间,我们得记录最后一个前缀和相等的结点。
        //只需要更新结点的下一个位置到最后一个前缀和相等的下一个位置即可。
        for(ListNode* cur = newhead; cur; cur = cur->next)
        {
            sum += cur->val;
            pos[sum] = cur;
        }
        sum = 0;
        for(ListNode* cur = newhead; cur; cur = cur->next)
        {
            sum += cur->val;
            cur->next = pos[sum]->next;
        }
        
        return newhead->next;
    }
};

2.链表内指定区间的反转

  • 思想:递归或者迭代,递归写起来较为简单,迭代需要三个指针,总的来说比光反转链表复杂了一些,因为还需要判断头结点和第一个结点反转之后链接的位置。

迭代:

class Solution {
public:

    ListNode* reverseBetween(ListNode* head, int m, int n) 
    {
        //非递归——空间复杂度为O(1)
        ListNode* newhead = head;
        ListNode* pprev = head;
        ListNode* prev = head,*cur = nullptr,*next = nullptr;

        if(prev)
            cur = prev->next;
        if(cur)
            next = cur->next;

        int m1 = m;
        while(--m1)
        {
            pprev = prev;
            prev = prev->next;
            
            if(prev)
            cur = prev->next;
            if(cur)
            next = cur->next;
        }


        int x = n - m ;

        ListNode *last = prev;
        while(x--)
            last = last->next;

        if(pprev == prev)
            newhead = last;
        else 
            pprev->next = last;

        last = last->next;
        
        int count = n - m;
        //指向n+1位置的结点
        prev->next = last;

        while(count--)
        {
            cur->next = prev;
            prev = cur;
            cur = next;

            if(next)
            next = next->next;
        }
         
        return newhead;
    }
};

递归:

class Solution 
{
public:
 
    void Reverse(ListNode* prev, ListNode* cur, int count)
    {
        if(count == 0)
            return;
        Reverse(cur,cur->next,count-1);

        cur->next = prev;
    }
    ListNode* reverseBetween(ListNode* head, int m, int n) 
    {
        //可以用递归试一下
        ListNode* cur =  head, *prev = cur;
        ListNode* Head;
        int m1 = m;
        while(--m1)
        {
            prev = cur;
            cur  = cur->next;
        }
        //找到 2 1


        ListNode* last = cur;

        int x = n - m;
        while(x--)
            last = last->next;


        if(cur != prev)
        {
            prev->next = last;
            Head = head;
        }
        else
            Head = last;

        last = last->next;
        

        Reverse(last,cur,n - m + 1);


        return Head;
    }
};

3.二叉树的坡度

  • 思想:后序遍历,对左右结点的坡度进行计算保存(绝对值),然后返回当前树的和即可。
class Solution {
public:
    int PrevOrder(TreeNode* root, vector<int>& v)
    {
        if(root == NULL)
            return 0;
        
        int left_sum = PrevOrder(root->left,v);
        int right_sum = PrevOrder(root->right,v);
        v.push_back(abs(left_sum - right_sum));
        return left_sum + right_sum + root->val;
    }
    int findTilt(TreeNode* root) 
    {
        vector<int> v;
        PrevOrder(root,v);
        int sum = 0;
        int n = v.size();
        for(int i = 0; i < n; i++)
        {
            sum += v[i];
        }
        return sum;
    }
};

4.从根到叶的二进制数之和

  • 思想:前序遍历,从根节点开始用移位操作符和异或操作符进行计算二进制的和,到叶子结点保存即可。
class Solution {
public:
    void PrevOrder(TreeNode*root ,vector<int>& v, int val)
    {
        if(root == nullptr)
            return;
        val = (val << 1) ^ (root->val);
        if(root->left == nullptr && root->right == nullptr)
        {
            v.push_back(val);
            return;
        }
        PrevOrder(root->left,v,val);
        PrevOrder(root->right,v,val);
    }
    int sumRootToLeaf(TreeNode* root) 
    {
        int i = 0;
        vector<int> v;
        PrevOrder(root, v, i);
        int sum = 0;
        int n = v.size();
        for(int i = 0; i < n; i++)
        {
            sum+=v[i];
        }
        return sum;
    }
};

5.二叉树的最近公共祖先

  • 思想:后序遍历,如果是q,p结点就返回此节点,然后根据根节点进行判断,是在两侧还是在一侧,否则看有没有p或者q结点,返回。
class Solution {
public:
    TreeNode* AfterOrder(TreeNode* root)
    {
        if(root == NULL)
            return NULL;
        
        TreeNode* left = AfterOrder(root->left);
        TreeNode* right = AfterOrder(root->right);

        //一侧和两端
        if((left && right) \
        ||(root == P && (left == Q || right == Q))\ 
        ||(root == Q &&(left == P || right == P)))
            Root = root;
        
        //根节点可能是P或者Q
        if(root == P || root == Q)
            return root;
        //剩余的情况肯定是两侧都为空,或者有一个不为空。
        return left != NULL ? left : right; 
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, \
    TreeNode* p, TreeNode* q) 
    {
        P = p;
        Q = q;
        AfterOrder(root);
        return Root;
    }
    TreeNode* P;
    TreeNode* Q;
    TreeNode* Root;
};

6.二叉搜索树与双向链表

  • 思想:中序遍历,关键是如何保留前一个节点,让当前节点指向前结点,当前结点指向后结点,这在递归中就需要用到引用。至于当前结点指向后一个结点,那不就是前一个节点指向当前结点么。
class Solution {
public:
	void InOrder(TreeNode* root,TreeNode*& prev)
	{
		if(root == nullptr)
			return;
		
		InOrder(root->left,prev);
		//根
		//根节点的左指针指向前驱。
		root->left = prev;
		//根节点的右指针指向后驱。
		if(prev)
			prev->right = root;
		//更新前一个节点
		prev = root;		

		InOrder(root->right,prev);
	}
    TreeNode* Convert(TreeNode* root) 
	{
		TreeNode* prev = nullptr;
		InOrder(root, prev);
		while(root && root->left)
		{
			root = root->left;
		}
		return root;
    }
};

八、其它

1.任务调度器

  • 原理:将任务分类,每次执行剩余任务量最多的,在其等待过程中用其余的任务填充即可。
class Solution {
public:
    struct Greater
    {
        bool operator()(const pair<char,int>& n1,const pair<char,int>& n2)
        {
            return n1.second > n2.second;
        }
    };
    int leastInterval(vector<char>& tasks, int n) 
    {
        map<char,int>count;
        for(auto e : tasks)
            count[e]++;

        vector<pair<char,int>> s(count.begin(),count.end());
        stable_sort(s.begin(),s.end(),Greater());

        vector<int> t;
        for(const auto& e : s)
        {
            t.push_back(e.second);
        }

        //关键每次执行的任务是最多的那一个。
        int time = 0;
        int cur = 0;
        int sz = t.size();
        while(t[cur])
        {
            if(t[cur])
                t[cur]--;
            time++;

            int gap = n;
            int i = cur + 1;
            int tmp = i;
            while(gap > 0)
            {
                while(i < sz && t[i] == 0)
                    i++;
                if(i < sz)
                {
                    
                    t[i++]--;
                }
                else
                {
                    if(t[cur] == 0)
                        break;
                }
                time++;
                gap--;
            }
            stable_sort(t.begin(),t.end(),greater());
        }

        return time;
    }
};
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值