ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

剑指Offer 第34-44题

2021-12-05 22:01:09  阅读:118  来源: 互联网

标签:val Offer int 44 nums next vector heap 34


AcWing 46. 二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。

如果是则返回true,否则返回false。

假设输入的数组的任意两个数字都互不相同。

样例
输入:[4, 8, 6, 12, 16, 14, 10]

输出:true

题解:

  1. 中序(二叉搜索树的中序是从小到大的结果)+后序建树
  2. 后序遍历最后一个数为根,二叉搜索树的性质,我们直接从头开始找到比根小的值,这便是左子树;右边部分便是右子树,如果不符合便是错误。
class Solution {
public:
    vector<int>  seq;
    bool verifySequenceOfBST(vector<int> sequence) {
        seq = sequence;
        if(seq.empty()) return true;
        return dfs(1, seq.size() - 1);
    }
    bool dfs(int l, int r){
        if(l >= r) return true;
        int root = seq[r];
        int  k = 0;
        while(k < r && seq[k] < root) k++;
        for(int i = k; i < r; i++)
        {A
            if(seq[i] < root) return false;
        }
        return dfs(l, k - 1) && dfs(k, r - 1);
    }
};

AcWing 47. 二叉树中和为某一值的路径

输入一棵二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。

从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

样例
给出二叉树如下所示,并给出num=22。
在这里插入图片描述
dfs

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> findPath(TreeNode* root, int sum) {
        if(root == NULL) return res;
        vector<int> path;
        dfs(root, sum, path);
        return res;
    }
    void dfs(TreeNode* root, int sum, vector<int>&path){
        if(root->left == NULL && root->right == NULL)
        {
            if(sum - root->val == 0)
            {
                path.push_back(root->val);
                res.push_back(path);
                path.pop_back();
            }
            return;
        }
        path.push_back(root->val);
        if(root->left != NULL) dfs(root->left, sum - root->val, path);
        if(root->right != NULL) dfs(root->right, sum - root->val, path);
        path.pop_back();
    }
};

AcWing 48. 复杂链表的复刻

请实现一个函数可以复制一个复杂链表。

在复杂链表中,每个结点除了有一个指针指向下一个结点外,还有一个额外的指针指向链表中的任意结点或者null。

注意:

函数结束后原链表要与输入时保持一致。

  1. hash表做映射 时间O(n) 空间O(n)
  2. 在这里插入图片描述
/**
 * Definition for singly-linked list with a random pointer.
 * struct ListNode {
 *     int val;
 *     ListNode *next, *random;
 *     ListNode(int x) : val(x), next(NULL), random(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *copyRandomList(ListNode *head) {
        if(!head) return head;
        unordered_map<ListNode*, ListNode*> pos;
        for(auto p = head; p; p = p -> next)
        {
            pos[p] = new ListNode(p->val);
        }
        
        pos[NULL] = NULL;
        for(auto p = head; p; p = p -> next)
        {
            pos[p]->next = pos[p->next];
            pos[p]->random = pos[p->random];
        }
        return pos[head];
    }
};
/**
 * Definition for singly-linked list with a random pointer.
 * struct ListNode {
 *     int val;
 *     ListNode *next, *random;
 *     ListNode(int x) : val(x), next(NULL), random(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *copyRandomList(ListNode *head) {
        // 每一个节点后面加复制
        for(auto p = head; p; p = p->next)
        {
            auto np = new ListNode(p->val);
            auto next = p->next;
            p->next = np;
            np->next = next;
            p = np;
        }
        
        // 复制random指针
        for(auto p = head; p; p = p->next->next)
        {
            if(p->random)
                p->next->random = p->random->next;
        }
        
        // 分离两个链表
        auto dummy = new ListNode(-1);
        auto cur = dummy;
        for(auto p = head; p; p = p->next)
        {
            cur->next = p->next;
            cur = cur->next;
            p->next = p->next->next;
        }
        return dummy->next;
    }
};

AcWing 49. 二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。

要求不能创建任何新的结点,只能调整树中结点指针的指向。

注意:

需要返回双向链表最左侧的节点。
例如,输入下图中左边的二叉搜索树,则输出右边的排序双向链表。
在这里插入图片描述
解题思路
实现返回每一个子树的两端节点即可

AcWing 50. 序列化二叉树

AcWing 51. 数字排列(good)

输入一组数字(可能包含重复数字),输出其所有的排列方式。

样例
输入:[1,2,3]

输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

题解

根以前的枚举不一样,需要用占位的思想去枚举,预先开出n个坑位,将nums每一个数往里面放,一个数只能放在前一个相同数字的后面。

class Solution {
public:
    int n = 0;
    vector<vector<int> > ans;
    vector<int> path;//位置
    //u表示枚举到nums第几个数了,start表示从空位的第几位开始可以放
    void dfs(int u, int start, int state, vector<int>& nums)
    {
        if(u == n)
        {
            ans.push_back(path);
            return;
        }
        
        // 头一个数或者与上一个数不同
        if(u == 0 || nums[u] != nums[u-1]) start = 0;
        for(int i = start; i < n; i++)
        {
            if(!(state>>i&1))
            {
                path[i] = nums[u];
                dfs(u + 1, i + 1, state+(1<<i), nums);
            }
        }
    }
    vector<vector<int>> permutation(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        n = nums.size();
        path.resize(n);
        dfs(0, 0, 0, nums);
    
        return ans;
    }
};

库函数

class Solution {
public:
    vector<vector<int>> permutation(vector<int>& nums) {
        sort(nums.begin(), nums.end());

        vector<vector<int>> res;
        do
        {
            res.push_back(nums);
        }while (next_permutation(nums.begin(),nums.end()));
        // do res.push_back(nums); while (next_permutation(nums.begin(), nums.end()));

        return res;
    }
};

AcWing 52. 数组中出现次数超过一半的数字

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

假设数组非空,并且一定存在满足条件的数字。

思考题:

假设要求只能使用 O(n) 的时间和额外 O(1) 的空间,该怎么做呢?
样例
输入:[1,2,1,1,3]

输出:1

题解

抵消(配对)的思想。
循环数组

  1. count == 0: value=a[i], count=1
  2. count != 0:
    • value != a[i] : count -= 1
    • value == a[i] : count += 1

n个村民
好人数量 > n/2 (回答的是正确)
坏蛋数量 < n/2 (回答随机)
o(n)次询问找出坏蛋。

count: 当前人被信任的次数,0
val: 人是谁,-1

从头循环,

  1. count == 0,首先问自己是否被信任,
    回答是好人,count += 1,val = id
    回答坏蛋的一定是坏蛋,好人只会回答自己是好人。
  2. count != 0, 问val十分是好人,若val是好人,则count += 1。
    若是坏人,则count -= 1。count减到0,则替换
    好人的信任值。

好人的信用量一定大于坏人的信用量,消耗一个好人肯定需要消耗一个坏人。

class Solution {
public:
    int moreThanHalfNum_Solution(vector<int>& nums) {
        int cnt = 0, val = -1;
        for(auto it: nums)
        {
            if(cnt == 0)
            {
                val = it;
                cnt ++;
            }
            else
            {
                if(val == it) cnt ++;
                else cnt --;
            }
        }
        return val;
    }
};

AcWing 53. 最小的k个数

输入n个整数,找出其中最小的k个数。

注意:

数据保证k一定小于等于输入数组的长度;
输出数组内元素请按从小到大顺序排序;
样例
输入:[1,2,3,4,5,6,7,8] , k=4

输出:[1,2,3,4]

用大根堆维护一下即可,(优先队列)

class Solution {
public:

    vector<int> getLeastNumbers_Solution(vector<int> input, int k) {
        priority_queue<int> heap;
        for(auto it : input)
        {
            
            if(heap.size() >= k)
            {
               
                if(it < heap.top())
                {
                    
                    heap.pop();
                    heap.push(it);
                }
            }
            else
                heap.push(it);
           
        }
        vector<int> res;
        for(int i = 0; i < k; i++)
        {
            res.push_back(heap.top());
            heap.pop();
        }
        reverse(res.begin(), res.end());
        return res;
    }
};
class Solution {
public:
    vector<int> getLeastNumbers_Solution(vector<int> input, int k) {
        priority_queue<int> heap;
        for (auto x : input)
        {
            if (heap.size() < k || heap.top() > x) heap.push(x);
            if (heap.size() > k) heap.pop();
        }
        vector<int> res;
        while (heap.size()) res.push_back(heap.top()), heap.pop();
        reverse(res.begin(), res.end());
        return res;
    }
};


AcWing 54. 数据流中的中位数

如何得到一个数据流中的中位数?

如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。

如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

样例
输入:1, 2, 3, 4

输出:1,1.5,2,2.5

解释:每当数据流读入一个数据,就进行一次判断并输出当前的中位数。

题解
维护两个堆,大根堆维护较小的那部分,小根堆维护较大那部分,保证大跟堆的数量最多只比小根堆的数量多1个即可,代码中实现的是比较简捷的做法。

class Solution {
public:
    priority_queue<int, vector<int>, greater<int> > heap_u;
    priority_queue<int> heap_d;
    int n = 0;
    void insert(int num){
        n++; 
        heap_d.push(num);
      
        while(heap_u.size() != 0 && heap_d.top() > heap_u.top())
        {
            int maxx = heap_d.top(), minn = heap_u.top();
            heap_d.pop(), heap_u.pop();
            heap_d.push(minn), heap_u.push(maxx);
        }
       
        
        if(heap_d.size() - heap_u.size() > 1)
        {
            heap_u.push(heap_d.top());
            heap_d.pop();
        }
    }

    double getMedian(){
        if(n & 1)
            return heap_d.top();
        else
            return (heap_d.top() + heap_u.top()) / 2.0;
    }
};

AcWing 55. 连续子数组的最大和

输入一个 非空 整型数组,数组里的数可能为正,也可能为负。

数组中一个或连续的多个整数组成一个子数组。

求所有子数组的和的最大值。

要求时间复杂度为O(n)。

样例
输入:[1, -2, 3, 10, -4, 7, 2, -5]

输出:18

题解
状态表示:
集合划分:以a[k] 为结尾的连续子数组

  1. 只有a[k]一个数
  2. 至少两个数,一定选a[k],所以可以去掉a[k]
    在这里插入图片描述
    相当于f(k-1) + a[k]

f(k) = max{a[k], f(k-1) +a[k]}
f(k) = a[k] + max(0, f(k-1))
S = a[k] + max(0, S)

AcWing 56. 从1到n整数中1出现的次数

标签:val,Offer,int,44,nums,next,vector,heap,34
来源: https://www.cnblogs.com/pxlsdz/p/15647311.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有