剑指offer30天打卡活动(day16 - day19)

目录

day16:

剑指 Offer 45. 把数组排成最小的数

剑指 Offer 61. 扑克牌中的顺子

day17:

剑指 Offer 40. 最小的 k 个数

剑指 Offer 41. 数据流中的中位数 

day18:

剑指 Offer 55 - I. 二叉树的深度

剑指 Offer 55 - II. 平衡二叉树

day19:

剑指 Offer 64. 求 1 + 2 + … + n

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

剑指 Offer 68 - II. 二叉树的最近公共祖先


day16:

一. 剑指 Offer 45. 把数组排成最小的数

题目:

输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

示例:

输入:[3,  30,  34,  5,  9]

输出:"3033459"

解题思路: 

求拼接数的大小问题可以转化成字符串的拼接问题。

拼接字符串:

1. x + y > y + x 可得:x 大于 y

2. x + y < y + x 可得:x 小于 y

例如:x =  "12" , y = "30" ,   "1230" < "3012"所以"12"排在"30"的前面。

C++代码:

class Solution {
public:
    string minNumber(vector& nums) {
        vector str;
        for(int i = 0; i < nums.size(); i++)
        {
            //to_string 将整数转化为字符串
            str.push_back(to_string(nums[i]));
        }
        quick_sort(str, 0, str.size()-1);
        string res;
        for(auto item: str)
            res.append(item);
        return res;
    }

private:
    void quick_sort(vector &str, int l, int r){
        if(l >= r) return ;
        int i = l - 1, j = r + 1;
        //正确
        string x = str[(l+r)/2];
        while(i < j)
        {
            do j--; while(str[j] + x > x + str[j]);
            do i++; while(str[i] + x < x + str[i]);
            
            if(i < j) swap(str[i], str[j]);
        }
        quick_sort(str, l, j);
        quick_sort(str, j+1, r);
    }
};

二. 剑指 Offer 61. 扑克牌中的顺子

题目:

从若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。

示例:

输入:[1, 2, 3, 4, 5]

输出:True

解题思路: 

将抽出来的五张牌进行排序,  循环遍历判读nums[i] = nums[i + 1] 是否成立进行判重。

C++代码:

class Solution {
public:
    bool isStraight(vector& nums) {
       sort(nums.begin(), nums.end());
       //存储大小王的个数
       int temp = 0; 
       for(int i = 0; i < 4; i++)
       {
           if(nums[i] == 0)
                temp++;
           else if(nums[i] == nums[i+1])
                return false;
       }
       
       return nums[4] - nums[temp] < 5;
    }
};

day17:

一. 剑指 Offer 40. 最小的 k 个数

题目:

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

示例:

输入:arr = [3, 2, 1], k = 2

输出:[1, 2] 或者 [2, 1]

解题思路: 

快排+遍历

快排模板:

void quick_sort(int q[], int l, int r)
{
    if (l >= r) return;

    int i = l - 1, j = r + 1, x = q[l + r >> 1];

    while (i < j)
    {
        do i ++ ; while (q[i] < x);
        do j -- ; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    }

    quick_sort(q, l, j);
    quick_sort(q, j + 1, r);
}

C++代码:

class Solution {
public:
    vector getLeastNumbers(vector& arr, int k) {
        quicksort(arr, 0, arr.size()-1);
        vector res;
        for(int i = 0; i < k; i++)
        {
            res.push_back(arr[i]);
        }
        return res;
    }

private:
    void quicksort(vector &arr, int l, int r){
        if(l >= r) return ;
        int i = l - 1, j = r + 1;
        int x = arr[(l + r) / 2];
        while(i < j)
        {
            do i++; while(arr[i] < x);
            do j--; while(arr[j] > x);
            if(i < j) swap(arr[i], arr[j]);
        }

        quicksort(arr, l, j);
        quicksort(arr, j+1, r);
    }
};

二. 剑指 Offer 41. 数据流中的中位数 

题目:

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

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

示例:

输入:

["MedianFinder",  "addNum",   "addNum",  "findMedian",  "addNum",  "findMedian"]
[[],  [1],  [2],  [],  [3],  []]

输出:[null,  null,  null,  1.50000,  null,  2.00000]

解释:

MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1);    // arr = [1]
medianFinder.addNum(2);    // arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3);    // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

解题思路: 

建立小顶堆 A 和大顶堆 B,其中 B 放较小的一半数、A 放较大的一半数

        如果 A.size() == B.size() ,则中位数为(A 的堆顶元素 + B 的堆顶元素) / 2

        如果 A.size() != B.size(),则中位数为A 的堆顶元素

小堆顶的建立:

priority_queuegreater> A;

大堆顶的建立:

priority_queueless> B;

C++代码:

class MedianFinder {
public:
    /** initialize your data structure here. */

    //升序队列, 小顶堆, 存储较大的一部分
    priority_queue, greater> A;
    //降序队列, 大顶堆, 存储较小的一部分
    priority_queue, less> B;

    MedianFinder() {

    }

    //从数据流中添加一个整数到数据结构中
    void addNum(int num) {
        if(A.size() == B.size())
        {
            B.push(num);
            A.push(B.top());
            B.pop();
        }
        else
        {
            A.push(num);
            B.push(A.top());
            A.pop();
        }

    }
    
    //返回目前所有元素的中位数
    double findMedian() {
        if(A.size() != B.size()) return A.top();
        else return (A.top() + B.top())/2.0;
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

day18:

一. 剑指 Offer 55 - I. 二叉树的深度

题目:

输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。

解题思路: 

利用队列对树进行遍历,每遍历一层,对计数器进行+1,遍历完整个树即可得到树的深度。

C++代码:

/**
 * 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:
    int maxDepth(TreeNode* root) {
        if(root == nullptr) return 0;

        queue A;
        A.push(root);
        int sum = 0;
        while(!A.empty())
        {
            int cnt = A.size();
            for(int i = 0; i < cnt; i++ )
            {
                TreeNode* node = A.front();
                A.pop();
                if(node -> left) A.push(node -> left);
                if(node -> right) A.push(node -> right);
            }
            sum++;
        }
        return sum;
    }
};

二. 剑指 Offer 55 - II. 平衡二叉树

题目:

输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

解题思路: 

思路是构造一个可以求树的深度的函数 depth(root)(如上面一题),通过比较某子树的左右子树的深度差 abs(depth(root  ->  left)  -  depth(root  ->  right))  <=  1 是否成立,来判断某子树是否是二叉平衡树。若所有子树都平衡,则此树平衡。

C++代码:

/**
 * 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:
    bool isBalanced(TreeNode* root) {
        if(root == nullptr) return true;
        int m = depth(root -> left);
        int n = depth(root -> right);
        if(abs(m- n) <= 1 && isBalanced(root -> left) && isBalanced(root -> right))
            return true;
        else
            return false;
    }

private:
    int depth(TreeNode* root){
        if(root == nullptr) return 0;

        queue A;
        A.push(root);

        int sum = 0;
        while(!A.empty())
        {
            int cnt = A.size();
            for(int i = 0; i < cnt; i++ )
            {
                TreeNode* node = A.front();
                A.pop();
                if(node -> left) A.push(node -> left);
                if(node -> right) A.push(node -> right);
            }
            sum++;
        }
        
        return sum;
    }
};

day19:

一. 剑指 Offer 64. 求 1 + 2 + … + n

题目:

求 1 + 2 + ... + n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

示例:

输入:n = 3

输出:6

解题思路: 

递归

由A && B得,

当A为真时才判断B是否为真,

当A为假时跳过B的。

所以将递归式子放在B中,A中放入递归的中止条件

C++代码:

class Solution {
public:
    int sumNums(int n) {
        (n > 1) && (n += sumNums(n-1));
        return n;
    }
};

二. 剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

题目:

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

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

示例:

输入:root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8

输出:6

解题思路: 

若root 是 p 和 q 的最近公共祖先,则只有一下三中情况:

  • p = root 且 q 在 root 的左子树或右子树中
  • q = root 且 p 在 root 的左子树或右子树中
  • p 和 q分别在 root 的左右子树中; 

因为该树为搜索二叉树,所以右节点均大于左节点可得:

  • 如果 root -> val  <  p -> val,表示 p 在 root 的右子树中;
  • 如果 root -> val  >  p -> val,表示 p 在 root 的左子树中;
  • 如果 root -> val  =  p -> val,表示 p 和 root 的指向同一节点

结合上述进行递归查找:

当 p 和 q 同时在 root 的左子树(或右子树)中, 则开启左子树(或右子树)的递归。

C++代码:

/**
 * 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:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root -> val < p -> val && root -> val < q -> val)
            return lowestCommonAncestor(root -> right, p, q);
        if(root -> val > p -> val && root -> val > q -> val)
            return lowestCommonAncestor(root -> left, p, q);
        return root;
    }
};

二. 剑指 Offer 68 - II. 二叉树的最近公共祖先

题目:

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

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

示例:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1

输出:3

解题思路: 

递归(回头补)

C++代码:

/**
 * 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:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        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;
        if(left == nullptr) return right;
        if(right == nullptr) return left;
        return root;
    }
};

 题目源自:

leetcode图解算法数据结构

你可能感兴趣的:(算法,c++)