常见面试编程题c++

编程

1、全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

https://leetcode-cn.com/problems/permutations/solution/

class Solution {
public:
    void backtrack(vector<vector<int>>& res, vector<int>& output, int first, int len){
        // 所有数都填完了
        if (first == len) {
            res.emplace_back(output);
            return;
        }
        for (int i = first; i < len; ++i) {
            // 动态维护数组
            swap(output[i], output[first]);
            // 继续递归填下一个数
            backtrack(res, output, first + 1, len);
            // 撤销操作
            swap(output[i], output[first]);
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int> > res;
        backtrack(res, nums, 0, (int)nums.size());
        return res;
    }
};		

2、螺旋矩阵

给你一个 mn 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

https://leetcode-cn.com/problems/spiral-matrix/

解题思路:
这里的方法不需要记录已经走过的路径,所以执行用时和内存消耗都相对较小

1、首先设定上下左右边界
2、其次向右移动到最右,此时第一行因为已经使用过了,可以将其从图中删去,体现在代码中就是重新定义上边界
3、判断若重新定义后,上下边界交错,表明螺旋矩阵遍历结束,跳出循环,返回答案
4、若上下边界不交错,则遍历还未结束,接着向下向左向上移动,操作过程与第一,二步同理
5、不断循环以上步骤,直到某两条边界交错,跳出循环,返回答案

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector <int> ans;
        if(matrix.empty()) return ans; //若数组为空,直接返回答案
        int u = 0; //赋值上下左右边界
        int d = matrix.size() - 1;
        int l = 0;
        int r = matrix[0].size() - 1;
        while(true)
        {
            for(int i = l; i <= r; ++i) ans.push_back(matrix[u][i]); //向右移动直到最右
            if(++ u > d) break; //重新设定上边界,若上边界大于下边界,则遍历遍历完成,下同
            for(int i = u; i <= d; ++i) ans.push_back(matrix[i][r]); //向下
            if(-- r < l) break; //重新设定有边界
            for(int i = r; i >= l; --i) ans.push_back(matrix[d][i]); //向左
            if(-- d < u) break; //重新设定下边界
            for(int i = d; i >= u; --i) ans.push_back(matrix[i][l]); //向上
            if(++ l > r) break; //重新设定左边界
        }
        return ans;
    }
};

3、反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

https://leetcode-cn.com/problems/reverse-linked-list/

1、定义两个指针:pre和cur;pre在前 cur在后。
2、每次让 cur的next指向pre实现一次局部反转
3、局部反转完成之后,pre和 cur同时往前移动一个位置
4、循环上述过程,直至 cur到达链表尾部
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
           ListNode* cur=head;
           ListNode* pre=nullptr;
           ListNode* temp=nullptr;
           while(cur!=nullptr){
               temp=cur->next;//记录当前节点的下一个节点
               cur->next=pre;//然后将当前节点指向pre
               pre=cur;//pre和cur节点都前进一位
               cur=temp;
           }
           return pre;
    }
};

4、反转链表 II

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

https://leetcode-cn.com/problems/reverse-linked-list-ii/

img1.png

img2.png

class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) {
        // 定义一个dummyHead, 方便处理
        ListNode* dummy = new ListNode(0);      
        dummy->next = head;

        // 初始化指针
        ListNode* g = dummy;
        ListNode* p = dummy-> next;
        
        // 将指针移到相应的位置
        for(int i = 0; i < left -1; i++){
            g = g->next;
            p = p->next;
        }
        // 头插法插入节点
        for(int i = 0; i < right - left; i++){
            ListNode* remove = p->next;
            p->next = p->next->next;
            remove->next = g->next;
            g->next = remove;
        }
        return dummy->next;
    }
};

5、K 个一组翻转链表

https://leetcode-cn.com/problems/reverse-nodes-in-k-group/

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
        if (head == nullptr || head->next == nullptr){
            return head;
        }
        //定义一个假的节点。
        ListNode* dummy=new ListNode(0);
        //假节点的next指向head。
        // dummy->1->2->3->4->5
        dummy->next=head;
        //初始化pre和end都指向dummy。pre指每次要翻转的链表的头结点的上一个节点。end指每次要翻转的链表的尾节点
        ListNode* pre=dummy;
        ListNode* end=dummy;

        while(end->next!=nullptr){
            //循环k次,找到需要翻转的链表的结尾,这里每次循环要判断end是否等于空,因为如果为空,end.next会报空指针异常。
            //dummy->1->2->3->4->5 若k为2,循环2次,end指向2
            for(int i=0;i<k&&end != nullptr;i++){
                end=end->next;
            }
            //如果end==null,即需要翻转的链表的节点数小于k,不执行翻转。
            if(end==nullptr){
                break;
            }
            //先记录下end.next,方便后面链接链表
            ListNode* next=end->next;
            //然后断开链表
            end->next=nullptr;
            //记录下要翻转链表的头节点
            ListNode* start=pre->next;
            //翻转链表,pre.next指向翻转后的链表。1->2 变成2->1。 dummy->2->1
            pre->next=reverse(start);
            //翻转后头节点变到最后。通过.next把断开的链表重新链接。
            start->next=next;
            //将pre换成下次要翻转的链表的头结点的上一个节点。即start
            pre=start;
            //翻转结束,将end置为下次要翻转的链表的头结点的上一个节点。即start
            end=start;
        }
        return dummy->next;


    }
    //链表翻转
    // 例子:   head: 1->2->3->4
        ListNode* reverse(ListNode* head) {
         //单链表为空或只有一个节点,直接返回原单链表
        if (head == nullptr || head->next == nullptr){
            return head;
        }
        //前一个节点指针
        ListNode* pre = nullptr;
        //当前节点指针
        ListNode* cur = head;
        //下一个节点指针
        ListNode* next = nullptr;
        while (cur != nullptr){
            next = cure->next;//next 指向下一个节点,保存当前节点后面的链表。
            cur->next=pre;//将当前节点next域指向前一个节点   null<-1<-2<-3<-4
            pre = cur;//pre指针向后移动。pre指向当前节点。
            cur = next;//cure指针向后移动。下一个节点变成当前节点
        }
        return preNode;
    }
};

6、岛屿数量

https://leetcode-cn.com/problems/number-of-islands/

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。

class Solution {
public:
    int numIslands(vector<vector<char>>& nums) {
        int res = 0;
        int m = nums.size(),
            n = nums[0].size();
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (nums[i][j] == '1') {
                    dfs(nums, i, j);
                    res ++;
                }
            }
        }
        return res;
    }

    void dfs(vector<vector<char>>& nums, int i, int j) {
        if (i < 0 || j < 0 || i >= nums.size() || j >= nums[0].size() || nums[i][j] == '0')
            return;
        nums[i][j] = '0';
        dfs(nums, i + 1, j);
        dfs(nums, i, j + 1);
        dfs(nums, i - 1, j);
        dfs(nums, i, j - 1);
    }
};

7、最长回文串

给你一个字符串 s,找到 s 中最长的回文子串。

https://leetcode-cn.com/problems/longest-palindromic-substring/

class Solution {
public:
      string longestPalindrome(string s) {
        int n = s.size();
        if (n < 2) {
            return s;
        }
        int maxLen = 1;
        int begin = 0;
        // dp[i][j] 表示 s[i..j] 是否是回文串
        vector<vector<int>> dp(n, vector<int>(n));
        // 初始化:所有长度为 1 的子串都是回文串
        for (int i = 0; i < n; i++) {
            dp[i][i] = true;
        }
        // 递推开始
        // 先枚举子串长度
        for (int L = 2; L <= n; L++) {
            // 枚举左边界,左边界的上限设置可以宽松一些
            for (int i = 0; i < n; i++) {
                // 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
                int j = L + i - 1;
                // 如果右边界越界,就可以退出当前循环
                if (j >= n) {
                    break;
                }

                if (s[i] != s[j]) {
                    dp[i][j] = false;
                } else {
                    if (j - i < 3) {
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i + 1][j - 1];//只有 s[i+1:j−1] 是回文串,并且 s 的第 i 和 j 个字母相同时,s[i:j]才会是回文串。
                    }
                }

                // 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
                if (dp[i][j] && j - i + 1 > maxLen) {
                    maxLen = j - i + 1;
                    begin = i;
                }
            }
        }
        return s.substr(begin, maxLen);
    }
};

8、数组中的第K个最大元素(快排65)

https://leetcode-cn.com/problems/kth-largest-element-in-an-array/

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        // 优先队列可以用一个 for 循环解决哈。就是在 for 循环里面判断小顶堆里面的 size() 是否大于 k 个数,是的话就 poll() 出去;整个 for 循环结束之后剩下来的就是 k 个数的小顶堆。堆顶即第 k 大的数。
        // priority_queue<Type, Container, Functional>
        // Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。  STL里面默认用的是vector),Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆
       priority_queue<int,vector<int>, greater<int> > heap;
        for (int num : nums) {
            heap.emplace(num);
            if (heap.size() > k) { 
                heap.pop();   //此时k+1个
            }
        }
        return heap.top();
    }
};

快排之单边循环法
class Solution {
    public :
    int findKthLargest(vector<int>&nums, int k) {
        int len = nums.size();
        int left = 0;
        int right = len - 1;

        // 转换一下,第 k 大元素的下标是 len - k
        int target = len - k;

        while (true) {
            int index = partition(nums, left, right);
            if (index == target) {
                return nums[index];
            } else if (index < target) {
                left = index + 1;
            } else {
                right = index - 1;
            }
        }
    }

     * 对数组 nums 的子区间 [left..right] 执行 partition 操作,返回 nums[left] 排序以后应该在的位置
     * 在遍历过程中保持循环不变量的定义:
   
   int partition(vector<int>& nums, int left, int right) {
        int pivot = nums[left];
        int j = left;
        for (int i = left + 1; i <= right; i++) {
            if (nums[i] < pivot) {
                // j 的初值为 left,先右移,再交换,小于 pivot 的元素都被交换到前面
                j++;
                swap(nums, j, i);
            }
        }
        // 在之前遍历的过程中,满足 nums[left + 1..j] < pivot,并且 nums(j..i) >= pivot
        swap(nums, j, left);
        // 交换以后 nums[left..j - 1] < pivot, nums[j] = pivot, nums[j + 1..right] >= pivot
        return j;
    }

    void swap(vector<int>& nums, int index1, int index2) {
        int temp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = temp;
    }
}

快排之双边循环法
lass Solution {
     vector<int> sortArray(vector<int>& nums) {
        quickSort(nums,0,nums.size()-1);
        return nums;
    }
    //快速排序主函数
   void quickSort(vector<int>& nums,int low,int high){
        //递归终止条件
        if(low >= high){
            return;
        }
        int mid = partition(nums,low,high);
        quickSort(nums,low,mid-1);
        quickSort(nums,mid+1,high);
        
    }
    //分割
    //每次找到基准元素对应的排序位置
   int partition(vector<int>& nums,int start,int end){
        int pivot = nums[start];
        int left = start;//不能从start+1开始,否则当只有两个数时,无论大小都会交换
        int right = end;
        while(left != right){
            
            while(left < right && nums[right] > pivot){//也可以写nums[right]>=pivot无影响
                right--;
            }
            while(left < right && nums[left] <= pivot){
                left++;
            }
            //结束两个子循环会有两种情况,只有left小于right时,才进行交换
            if(left < right){
                swap(nums,left,right);
            }
        }
        //此时跳出while循环,只可能是left等于right,不会有left>right的情况
        //接下来的交换和返回,写left或者right都行,因为两者相等
        swap(nums,start,left);
        return left;
    }
   void swap(vector<int>& nums,int i,int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}


9、有效的括号

https://leetcode-cn.com/problems/valid-parentheses/

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。

class Solution {
public:
    bool isValid(string s) {
           unordered_map<char,int> m{{'(',1},{'[',2},{'{',3},
                                {')',4},{']',5},{'}',6}};
        stack<char> st;
        bool istrue=true;
        for(char c:s){
            int flag=m[c];
            if(flag>=1&&flag<=3) st.push(c);
            else if(!st.empty()&&m[st.top()]==flag-3) st.pop();
            else {istrue=false;break;}
        }
        if(!st.empty()) istrue=false;
        return istrue
    }
};

10、无重复字符的最长子串

https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        //    if(s.size()==0 ||s.size()==1) return s.size();哈希的有问题 aab
        //    if(s.size()==2 && s[0]==s[1]) return 1;
        //    if(s.size()==2 && s[0]!=s[1]) return 2;
        //    int a=0;
        //    for(int i=0;i<s.size();i++){
        //        unordered_map<char,int> dic;
        //        for(int j=i;j<s.size();j++){
        //            if(dic.find(s[j])!=dic.end()){
        //                if(j-i>a) a=j-i;
        //                break;
        //            }
        //             dic[s[j]]++;
        //        }
        //    }
        //    return a;
        if(s.size() == 0) return 0;
        unordered_set<char> lookup;
        int maxStr = 0;
        int left = 0;
        for(int i = 0; i < s.size(); i++){
            while (lookup.find(s[i]) != lookup.end()){
                lookup.erase(s[left]);//不断从左缩小窗口
                left ++;
            }
            maxStr = max(maxStr,i-left+1);
            lookup.insert(s[i]);
        }
        return maxStr;
    }
};

11、买卖股票的最佳时机

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/)

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        // int len = prices.size();
        // int res = 0;
        // // 前一天卖出可以获得的最大利润
        // int pre = 0;
        // for (int i = 1; i < len; i++) {
        //     // 利润差
        //     int diff = prices[i] - prices[i - 1];
        //    // 状态转移方程:第i天卖出可以获得的最大利润 = 第i-1天卖出的最大利润 + 利润差
        //     pre = max(pre + diff, 0);
        //     res = max(res, pre);
        // }
        // return res;
       // 动态规划。空间复杂度为O1,时间复杂度为On
       // 标记一个买入股票的日子的股价记作left,然后尝试在某一天卖出股票,策略如下:
       // 如果这一天的股价高于left,则可以考虑卖出股票,利润暂存到res里。
       // 如果这一天的股价小于等于left,则应该选择这一天作为买入股票的日子,此时的暂存的利润要么是0,要么是之前暂存的值。

        int left = 10000000;
        int res = 0;
        for(int i : prices){
            if(left >= i){
                left = i;
            }else if(left < i){
                res = max(i-left,res);
            }
        }
        return res;
    }
};

12、相交链表

https://leetcode-cn.com/problems/intersection-of-two-linked-lists/

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *node1 = headA;
        ListNode *node2 = headB;
        
        while (node1 != node2) {
            node1 = (node1 != NULL) ? node1->next : headB;
            //巧妙:是判断(node1 != NULL) ?,而不是(node1-next != NULL) ?  这样不会陷入死循环,比如在B的最后一个节点,node1 != NULL,但是node1->next 为Null,然后赋个node1,而下一步同样node2也走到了A的最后一个节点,所以node2也=Null,所以下一次while的判断不成立,跳出循环,相当于指向了同一个相同的节点Null(死亡),两个人都走了L1+L2的长度,所以最后到终点的时间也相同,不会出现死循环。用(node1-next != NULL) ?  会导致node永远不会指向null
            node2 = (node2 != NULL) ? node2->next : headA;
        }
        return node1;
        // 两个链表长度分别为L1+C、L2+C, C为公共部分的长度,第一个人走了L1+C步后,回到第二个人起点走L2步;第2个人走了L2+C步后,回到第一个人起点走L1步。 当两个人走的步数都为L1+L2+C时就两个家伙就相遇相爱了
        // 你变成我,走过我走过的路。我变成你,走过你走过的路。然后我们便相遇了
    }
};

13、两数之和

https://leetcode-cn.com/problems/two-sum/

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 target 的那两个整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> dic;
        for(int i=0;i<nums.size();++i){
            auto it=dic.find(target-nums[i]);
            if(it!=dic.end()) {
                return {i,it->second};
            }
            dic[nums[i]]=i;
        }
        return {};
    }
};
两数相加

https://leetcode-cn.com/problems/add-two-numbers/

给你两个非空的链表,表示两个非负的整数。它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* head=new ListNode(-1);//存放结果的链表
        ListNode* h=head;//移动指针
        int sum=0;//每个位的加和结果
        bool carry=false;//进位标志
        while(l1!=NULL||l2!=NULL)
        {
            sum=0;
            if(l1!=NULL)
            {
                sum+=l1->val;
                l1=l1->next;
            }
            if(l2!=NULL)
            {
                sum+=l2->val;
                l2=l2->next;
            }
            if(carry)
                sum++;
            h->next=new ListNode(sum%10);
            h=h->next;
            carry=sum>=10?true:false;
        }
        if(carry)   //如果最后还有一位进位
        {
            h->next=new ListNode(1);
        }
        return head->next;
    }
};

14、三数之和

https://leetcode-cn.com/problems/3sum/

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

class Solution { //排序,双指针
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans;
        if (nums.size() < 3) return ans;
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] > 0) return ans;
            // 去重
            if ( i > 0 && nums[i] == nums[i - 1]) {//为什么要大于0?,防止在i=0是判断出错
                continue;
            }
            int left = i + 1;
            int right = nums.size() - 1;
            while (left < right) {
                if(nums[left] + nums[right] + nums[i] > 0) {
                    right--;
                } else if(nums[left] + nums[right] + nums[i] < 0){
                    left++;
                } else {
                    ans.push_back({nums[left], nums[right], nums[i]});
                    while (right > left && nums[right] == nums[right - 1]) right--;  //去掉重复的的
                    while (right > left && nums[left] == nums[left + 1]) left++;    
                    left++;//前两个循环结束,是指向在重复元素的最后一位
                    right--;
                }
            }
        }
        return ans;
    }
};

15、最大子序和

https://leetcode-cn.com/problems/maximum-subarray/

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

class Solution {
public:
    // 动态规划
    int maxSubArray(vector<int>& nums) {
        //dp[i]表示nums中以nums[i]结尾的最大子序和
        vector<int> dp(nums.size());
        dp[0] = nums[0];
        int result = dp[0];
        for (int i = 1; i <nums.size(); i++)
        {
            dp[i] = max(dp[i - 1] + nums[i], nums[i]);
            result = max(result, dp[i]);
        }
        return result;
    }
    // 贪心
    int maxSubArray(vector<int>& nums) {
        //类似寻找最大最小值的题目,初始值一定要定义成理论上的最小最大值
        int result = INT_MIN;
        int numsSize = int(nums.size());
        int sum = 0;
        for (int i = 0; i < numsSize; i++)
        {
            sum += nums[i];
            result = max(result, sum);
            //如果sum < 0,重新开始找子序串
            if (sum < 0)
            {
                sum = 0;
            }
        }

        return result;

    }
};

16、环形链表

https://leetcode-cn.com/problems/linked-list-cycle/

给定一个链表,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

如果链表中存在环,则返回 true 。 否则,返回 false 。

class Solution {
public:
    bool hasCycle(ListNode *head) {
        //当一个链表有环时,快慢指针都会陷入环中进行无限次移动,然后变成了追及问题。想象一下在操场跑步的场景,只要一直跑下去,快的总会追上慢的。当两个指针都进入环后,每轮移动使得慢指针到快指针的距离增加一,同时快指针到慢指针的距离也减少一,只要一直移动下去,快指针总会追上慢指针。
        ListNode *slow = head;
        ListNode *fast = head;
        while(fast != nullptr) {
            fast = fast->next;
            if(fast != nullptr) {
                fast = fast->next;
            }
            if(fast == slow) {
                return true;
            }
            slow = slow->next;
        }
        return false;
       //如果存在环,如何判断环的长度呢?方法是,快慢指针相遇后继续移动,直到第二次相遇。两次相遇间的移动次数即为环的长度。
    }
};
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *slow=head,*fast=head;
        while(1){
            if(fast ==nullptr || fast->next ==nullptr) return nullptr;
            fast=fast->next->next;
            slow=slow->next;
            if(fast==slow){  //第一次相遇,这时fast走了2nb,slow走了nb
                fast=head;
                break;
            }
        }
        while(fast != slow){   //让fast和slow走a,这时他们在入环的第一个节点相遇,
            fast=fast->next;
            slow=slow->next;
        }
        return fast;
    }
};

17、合并两个有序链表

https://leetcode-cn.com/problems/merge-two-sorted-lists/

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

这道题可以使用递归实现,新链表也不需要构造新节点,下面列举递归三个要素
终止条件:两条链表分别名为 l1 和 l2,当 l1 为空或 l2 为空时结束
返回值:每一层调用都返回排序好的链表头
本级递归内容:如果l1的 val值更小,则将 l1->next 与排序好的链表头相接,l2 同理
O(m+n),m为l1的长度,n为 l2 的长度
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if (l1 == NULL) {
            return l2;
        }
        if (l2 == NULL) {
            return l1;
        }
        if (l1->val <= l2->val) {
            l1->next = mergeTwoLists(l1->next, l2);
            return l1;
        }
        l2->next = mergeTwoLists(l1, l2->next);
        return l2;

    }
};

18、二叉树的层序遍历

https://leetcode-cn.com/problems/binary-tree-level-order-traversal/

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。(即逐层地,从左到右访问所有节点)

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        queue<TreeNode*> queue;
        if (root != nullptr) queue.push(root);
        while (!queue.empty()) {
           int n = queue.size();  // 无法区分队列中的结点来自哪一层。因此,我们需要稍微修改一下代码,在每一层遍历开始前,先记录队列中的结点数量 n(也就是这一层的结点数量),然后一口气处理完这一层的 n 个结点。
            vector<int> level;
            for (int i = 0; i < n; ++i) {
                TreeNode* node = queue.front();
                queue.pop();
                level.push_back(node->val);
                if (node->left != nullptr) queue.push(node->left);
                if (node->right != nullptr) queue.push(node->right);
            }
            res.push_back(level);
        }
        return res;
    }
};

19、二叉树的锯齿形层序遍历

https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/

给定一个二叉树,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

class Solution {
public: 
    // 解题思路:每层遍历塞到数组里,那每层的一维数组的值是固定的,所以只要往这层塞数据就可以了
    // 根据题意,每层从左往右,下层从右往左,所以每偶数层添加在后面,每奇数层插入在前面,递归即可
    void Sum(vector<vector<int>>& arr, TreeNode* root, int row)
    {
        if(root == nullptr)
            return;
        if(arr.size()<= row)
            arr.push_back(vector<int>());
        if(row%2 == 0)
            arr[row].push_back(root->val);
        else
            arr[row].insert(arr[row].begin(), root->val);
        Sum(arr, root->left, row+1);
        Sum(arr, root->right, row+1);
    }
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> arr;
        Sum(arr, root, 0);
        return arr;
    }
   
};

20、合并两个有序数组

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

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

  1. 思路的重点一个是从后往前确定两组中该用哪个数字
  2. 另一个是结束条件以第二个数组全都插入进去为止
class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int i = nums1.size() - 1;
        m--;
        n--;
        while (n >= 0) {
        while (m >= 0 && nums1[m] > nums2[n]) {
            swap(nums1[i--], nums1[m--]);
        }
        swap(nums1[i--], nums2[n--]);
    }

   
    //     int i = nums1.size() - 1;
    //     --m;
    //     --n;
    //     while (n >= 0)
    //     {
    //         if(m >= 0 && nums1[m] > nums2[n])
    //         {
    //             nums1[i--] = nums1[m--];
    //         }
    //         else
    //         {
    //             nums1[i--] = nums2[n--];
    //         }
    //     }
}
};

21、二叉树的最近公共祖先

https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
   //1 当 left 和 right 同时为空 :说明 root 的左 / 右子树中都不包含 p,q ,返回 null ;
   //2 当 left 和 right 同时不为空 :说明 p,q 分列在 root 的 异侧 (分别在 左 / 右子树),因此 root为最近公共祖先,返回  root ;
   //3 当 left为空 ,right 不为空 :p,q都不在 root的左子树中,直接返回 right。具体可分为两种情况:
   //    p,q 其中一个在 root 的 右子树 中,此时 right 指向 p(假设为 p);
   //    p,q 两节点都在 root 的 右子树 中,此时的 right 指向 最近公共祖先节点 ;
   //4 当 left 不为空 , right 为空 :与情况 3. 同理;

        if(root == nullptr || root == p || root == q) return root;
        TreeNode *left = lowestCommonAncestor(root->left, p, q);
        TreeNode *right = lowestCommonAncestor(root->right, p, q);
        if(left == nullptr && right == nullptr) return nullptr; // 1.
        if(left == nullptr) return right;// 3.
        if(right == nullptr) return left;// 4.
        return root;// 2. if(left != null and right != null)
    }
};

22、颜色填充

红,绿,蓝三种颜色,各自n个,相邻不能一样(首位不能一样),求填充方法数

对于我们可以选择红绿蓝三种颜色,我们可以将它们看成 0, 1, 2。这样一来,一种涂色方案就对应着一个长度为 m 的三进制数,其十进制的范围为 [0, 3^m)。

我们可以枚举 [0, 3^m)数,将其转换为长度为 m 的三进制串,再判断其是否满足任意相邻的两个数位均不相同即可。

int main() {
    unordered_map<int, vector<int>> valid;
    int m = 3;
    // 在 [0, 3^m) 范围内枚举满足要求的 mask
    int mask_end = pow(3,3);
    for (int mask = 0; mask < mask_end; ++mask) {
        vector<int> color;
        int mm = mask;
        for (int i = 0; i < m; ++i) {
            color.push_back(mm % 3);
            mm /= 3;
        }
        bool check = true;
        for (int i = 0; i < m - 1; ++i) {
            if (color[i] == color[i + 1]) {
                check = false;
                break;
            }
        }
        if (check) {
            valid[mask] = move(color);
        }
    }
    cout << valid.size();
    return 0;
}

23、搜索旋转排序数组

https://leetcode-cn.com/problems/search-in-rotated-sorted-array/

整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

class Solution {
public:
    int search(vector<int>& nums, int target) {
          if(nums.size()==0){
              return -1;
          }
          int left=0,right=nums.size()-1;
          while(left+1<right){
              int mid=left+(right-left)/2;
              if(nums[mid]==target){
                  return mid;
              }
              
              if(nums[left]<nums[mid]){
                  if((nums[mid]>=target)&&(target>=nums[left])){
                      right=mid;
                  }else {
                      left=mid;
                  }
              }

              if(nums[right]>nums[mid]){
                  if((nums[mid]<=target)&&(target<=nums[right])){
                      left=mid;
                  }else{
                      right=mid;
                  }
              }
          }
          
          if (nums[left] == target ){
                 return left;
             } 
          if(nums[right] == target) {
                 return right;
             }
          return -1;
             
    }
};

24、x的平方根

给你一个非负整数 x ,计算并返回 x算术平方根

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3LqCGAbo-1649167843204)(C:\Users\hgx\AppData\Roaming\Typora\typora-user-images\image-20211012143335307.png)]

class Solution {
public:
    int mySqrt(int x) {
        if (x == 0) {
            return 0;
        }
        int ans = exp(0.5 * log(x));
        return ((long long)(ans + 1) * (ans + 1) <= x ? ans + 1 : ans);
    }
};
二分查找
class Solution {
public:
    int mySqrt(int x) {
        int l = 0, r = x, ans = -1;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if ((long long)mid * mid <= x) {
                ans = mid;
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
        return ans;
    }
};
class Solution {
public:
    int s;
    int mySqrt(int x) {
       s=x;
       if(x==0) return 0;
       return ((int)(sqrts(x)));
    }

    double sqrts(double x){
       double res = (x + s / x) / 2;
       if (res == x) {
          return x;
       } else {
          return sqrts(res);
     }
    } 
};

25、链表中倒数第k个节点

https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

class Solution {
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
        ListNode *fast = head, *slow = head;
        for(int i = 0; i < k; i++) {
            if(fast == nullptr) return nullptr;
            fast = fast->next;
        }
        while(fast != nullptr) {
            fast = fast->next;
            slow = slow->next;
        }
            return slow;
    }   
};

26、删除链表的倒数第 k 个节点

https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;    //等同于ListNode* dummy = new ListNode(0, head);

        ListNode* p = dummyHead;
        ListNode* q = dummyHead;
        for( int i = 0 ; i < n + 1 ; i ++ ){
            q = q->next;
        }

        while(q){
            p = p->next;
            q = q->next;
        }

        ListNode* delNode = p->next;
        p->next = delNode->next;
        delete delNode;

        ListNode* retNode = dummyHead->next;
        delete dummyHead;

        return retNode;
        
    }
};
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值