LeetCode[138] Copy List With Random Pointer 解法及代码(c++)

题目描述

A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.

Return a deep copy of the list.

The Linked List is represented in the input/output as a list of n nodes. Each node is represented as a pair of [val, random_index] where:

val: an integer representing Node.val
random_index: the index of the node (range from 0 to n-1) where random pointer points to, or null if it does not point to any node.

Example 1:

Input: head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
Output: [[7,null],[13,0],[11,4],[10,2],[1,0]]

Example 2:

Input: head = [[1,1],[2,1]]
Output: [[1,1],[2,1]]
Example 3:

Input: head = [[3,null],[3,0],[3,null]]
Output: [[3,null],[3,0],[3,null]]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/copy-list-with-random-pointer
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解法

分析一下,因为有随机指针,所以遍历一遍不能完成随机指针的复制(如果随机指针指向链表后部的节点,在遍历一遍的情况下,新链表无法得知后部节点)。我们需要遍历两遍,遍历第一遍时,我们需要将随机指针的index记录下来,同时将新节点按顺序保存在vector,第二遍遍历新链表,在对应的index获得random值。

代码

 
Node* copyRandomList(Node* head) {
 if (!head) return NULL;
 unordered_map<Node*, int> node_index;
 vector<Node*> node_vector;
 Node* node = head;
 int index = 0;
 Node* parent = NULL;
 Node* new_head = NULL;
 while (node) {
   node_index.insert(make_pair(node, index));
   Node* new_node = new Node(node->val);
   if (parent)
   parent->next = new_node;
   node_vector.push_back(new_node);
   if (parent == NULL)
     new_head = new_node;
   parent = new_node;
   node = node->next;
   index++;
 }
 node = new_head;
 auto origin_node = head;
 while (node) {
   auto r = node_index.find(origin_node->random);
   if (r != node_index.end()) {
     index = r->second;
   }
   else {
     index = -1;
   }
   node->random = index >= 0 ? node_vector[index] : NULL;
   node = node->next;
   origin_node = origin_node->next;
 }
 return new_head;
}

运行结果

执行用时 :16 ms, 在所有 C++ 提交中击败了60.63%的用户
内存消耗 :13.7 MB, 在所有 C++ 提交中击败了69.36%的用户

我原以为空间开得挺多,结果比想象的要好

LeetCode[70] Climbing Stairs 的解法及代码(c++)

题目描述

You are climbing a stair case. It takes n steps to reach to the top.

Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Note: Given n will be a positive integer.

Example 1:

Input: 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps
Example 2:

Input: 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/climbing-stairs
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解法

这道题是一道简单难度的题目,但是很经典。假设当前是第n级台阶,因为可以迈1步或者2步,所以迈上第n级台阶的方法是迈上第n-1级台阶的方法和迈上第n-2级台阶方法的和。所以显然可以用递归或动态规划法。

递归

根据上述,第n级台阶,都可以递归到第1级台阶和第2级台阶的和。同时,在递归的过程中,我们存下已经计算过的n级台阶的结果,可以快速返回,不用每次都递归到1和2.

代码

    int climbStairsInternal(int n, unordered_map<int, int>& cache) {
        if (n == 0) return 1;
        if (n <= 2) return n;
        auto r = cache.find(n);
        if (r != cache.end()) {
            return r->second;
        }
        int one_step = climbStairsInternal(n - 1, cache);
        int two_step = climbStairsInternal(n - 2, cache);
        cache.insert(make_pair(n, one_step + two_step));
        return one_step + two_step;
    }
    int climbStairs(int n) {
        unordered_map<int, int> cache;
        if (n == 0) return 1;
        if (n <= 2) return n;
        int one_step = climbStairsInternal(n - 1, cache);
        int two_step = climbStairsInternal(n - 2, cache);
        return one_step + two_step;
    }

执行结果

执行用时 :8 ms, 在所有 C++ 提交中击败了61.43%的用户
内存消耗 :8.9 MB, 在所有 C++ 提交中击败了5.12%的用户

动态规划

根据前面的分析,我们设dp[n]为第n级台阶的方法,则dp[n] = dp[n-1] + dp[n-2], 特殊的dp[0] = 1, dp[1] = 1, dp[2] = 2

代码


int climbStairs(int n) {
  vector<int> dp(n + 1, 0);
  if (n == 0) return 1;
  dp[0] = 1;
  dp[1] = 1;
  for (int i = 2; i <= n; i++) {
    dp[i] = dp[i - 1] + dp[i - 2];
  }
  return dp[n];
}

执行结果

执行用时:4 ms
内存消耗:8.6 MB
可以看到,用时能快一些。但空间复杂度是一样的,在5%,还有优化的空间,如何能够提高空间利用率。

LeetCode[102] Binary Tree Level Order Traversal 的解法与代码(c++)

题目描述

Given a binary tree, return the level order traversal of its nodes’ values. (ie, from left to right, level by level).

For example:
Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
return its level order traversal as:
[
[3],
[9,20],
[15,7]
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解法

题目要求按层遍历,传统的DFS也可以解,但是没有那么自然。我们用一个deque(可以先进先出), 按顺序压入节点,设level_count为每层的节点数,vector v为每层的节点值集合,每访问一个节点操作:level_count –;v.push_back(node->val); deque.pop_front();,当level_count == 0时,说明一层已经访问完毕, 操作:result.push_back(v); v.clear(); 更新level_count = 下一层节点数;

代码

void traversalNext(TreeNode* node, deque& node_stack) {
    if (node->left) {
        node_stack.push_back(node->left);
    }
    if (node->right) {
        node_stack.push_back(node->right);
    }
}

vector> levelOrder(TreeNode* root) {
    vector<vector<int>> result;
    vector<int> v;
    int level_count = 0;
    if (root == NULL) return result;
    deque<<TreeNode*> node_stack;
    node_stack.push_back(root);
    level_count = node_stack.size();
    while (!node_stack.empty()) {
        TreeNode* node = node_stack.front();
        v.emplace_back(node->val);
        node_stack.pop_front();
        level_count--;
        if (level_count == 0) {
            result.push_back(v);
            v.clear();
            traversalNext(node, node_stack);
            level_count = node_stack.size();
        }
        else {
            traversalNext(node, node_stack);
        }
    }
    return result;
}

扩展

上面说过,这道题也是可以用DFS解的,只是初看不那么自然。分享我的一段面试经历,面试官出了这么一道题,给出一个二叉树的左视图,左视图就是从树的左边往右看,看到的节点。转换一下,就是每层的第一个节点。我当时第一反应就是DFS遍历树,并给出了解法。面试官感觉水平不高,没看太明白(连

vector<vector<int>> result;

这种结构都不是很清楚),让我用循环迭代,我居然也没给出来,真是悔恨…

DFS的解法

依然是vector<vector<int>> result; 存放结果,
int level;当前的层数,
然后前序遍历DFS,对于面试题,我们设i为层,只要依次返回result[i][0]即可。

代码

void traversalDFS(TreeNode* node, int level, vector<vector<int>>& result) {
    if (node == NULL) {
        return;
    }
    if (level > result.size())
        result.push_back(vector());
    result[level-1].push_back(node->val);
    traversalDFS(node->left, level + 1, result);
    traversalDFS(node->right, level + 1, result);
}

vector<vector<int>> levelOrder(TreeNode* root) {
    vector<vector<int>> result;
    traversalDFS(root, 1, result);
    return result;
}

效率:实际上这个解法的效率比一开始给出的伪迭代解法效率要高,也是出乎我的意料,分析一下,应该是伪迭代用了deque来模拟栈,还不如正统的栈来得快。附上leetcode提交结果:
执行用时 : 4 ms, 在所有 C++ 提交中击败了 92.97%的用户
内存消耗 : 14.9 MB, 在所有 C++ 提交中击败了11.32%的用户
伪迭代只有60%, 但是内存消耗要低一些。

LeetCode[518] Coin Change 2 解法及代码(C++)

题目描述

You are given coins of different denominations and a total amount of money. Write a function to compute the number of combinations that make up that amount. You may assume that you have infinite number of each kind of coin.

Example 1:

Input: amount = 5, coins = [1, 2, 5]
Output: 4
Explanation: there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
Example 2:

Input: amount = 3, coins = [2]
Output: 0
Explanation: the amount of 3 cannot be made up just with coins of 2.
Example 3:

Input: amount = 10, coins = [10]
Output: 1

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-change-2
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

有一堆各种面额的硬币coins,给定一个数量,求用不同面额硬币组成这个数量的方法数。每种面额使用数量不限。

解法

这是一道典型的动态规划题,其特征是当前的状态能够由上一个状态推得。
有基本的思想模板:
* 定义答案显而易见的基本情况。
* 制定根据简单的情况计算复杂情况的策略。
* 将此策略链接到基本情况。(by leetcode中国)
我们coins = {1,2,5},列表

 

先列出coins = {} 的情况,这是答案显而易见的基本情况,需要稍微注意一下的就是amount = 0时,结果为1,即不选。
添加进一种面额 1,coins = {1}, 所有结果均为1.
添加进一种面额2,coins = [1,2}, 考虑如何从没有2时的情况推得,dp[amount] = dp[amount-2](选入2,即2所在的这一行) + dp[amount](没有选入2, 即上一行), 可得递推式, 当amount >= coin 有 dp[amount] += dp[amount-coin]
添加进一种面额5, coins = {1,2,5}, 用递推式得出1-5的结果,容易验证结果正确。

代码

int change(int amount, vector& coins) {
    std::sort(coins.begin(), coins.end(), greater());
    vector dp(amount + 1, 0);
    dp[0] = 1;
    for (auto coin : coins) {
        for (int i = coin; i <= amount; i++)
            dp[i] += dp[i-coin];
    }
    return dp[amount];
}

LeetCode[230] Kth Smallest Element In a BST 解法及代码

题目描述

Given a binary search tree, write a function kthSmallest to find the kth smallest element in it.

Note:
You may assume k is always valid, 1 ≤ k ≤ BST’s total elements.

Example 1:

Input: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
  2
Output: 1
Example 2:

Input: root = [5,3,6,2,4,null,null,1], k = 3
5
/ \
3 6
/ \
2 4
/
1
Output: 3
Follow up:
What if the BST is modified (insert/delete operations) often and you need to find the kth smallest frequently? How would you optimize the kthSmallest routine?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kth-smallest-element-in-a-bst
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

找出BST(二叉查找树)第K小的元素

解法

BST 也就是满足 左子树 < 根 < 右子树 条件的二叉树。因此,我们用中序遍历即可以得到从小到大排列的元素数组,题目要求返回第K小的元素,即我们只要遍历到第K个元素即可。 用常规的递归遍历代码实现快,但是程序运行速度较慢(中国版leetcode 10%),用迭代法遍历代码实现有难度,比较考验代码功底,但程序运行速度快(中国版Leetcode 70%)。

代码

 // 递归
 void traversalInorder(TreeNode* root, vector& result, int k) {
     if (result.size() == k || root == NULL) {
         return;
     }
     traversalInorder(root->left, result, k);
     result.emplace_back(root->val);
     traversalInorder(root->right, result, k);
 }

 // 迭代
void traversalInorder(TreeNode* root, vector& result, int k) {
    stack stack_;
    auto node = root;
    while(node || !stack_.empty()) {
        while(node) {
            if (node->left) {
                stack_.push(node);
                node = node->left;
            } else {
                result.emplace_back(node->val);
                if (result.size() == k)
                    return;
                node = node->right;
            }
        }
        if (!stack_.empty()) {
            node = stack_.top();
            stack_.pop();
            result.emplace_back(node->val);
            if (result.size() == k)
                return;
            node = node->right;
        }
    }
}

int kthSmallest(TreeNode* root, int k) {
    vector result;
    traversalInorder(root, result, k);
    return result[k-1];    
}

LeetCode[148]Sort List 解法及代码

题目描述:

Sort a linked list in O(n log n) time using constant space complexity.

Example 1:

Input: 4->2->1->3
Output: 1->2->3->4
Example 2:

Input: -1->5->3->4->0
Output: -1->0->3->4->5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解法

题目要求算法复杂度是O(nlogn),故不能使用冒泡和插入排序,因为是链表不能随机寻址,所以只能使用归并排序。

链表有两个基本操作需要先实现,拆分(split)和合并(merge), 用这两个基本操作就可实现算法。

拆分(split): 给一个head和n, 拆成n个元素的左链表,剩下为右链表,返回右链表
如:1->2->3, 1; 返回2->3;

合并(merge): 给两个链表,按从小到大的顺序合并成一个链表,并返回
如:1->3, 2->4; 1->2->3->4;

则题目所给4->2->1->3, 先拆成4,2,1,3, 然后归并2->4, 1->3, 1->2->3->4

因为题目又要求只能用常量空间,所以不能开额外的容器存拆出来的元素,对编码技巧有一定要求。

代码

  ListNode* merge(ListNode* l1, ListNode* l2) {
    ListNode dummyHead(0);
    ListNode* p = &dummyHead;
    while (l1 && l2) {
      if (l1->val < l2->val) {
        p->next = l1;
        l1 = l1->next;
      } else {
        p->next = l2;
        l2 = l2->next;
      }
      p = p->next;
    }
    p->next = l1 ? l1 : l2;

    return dummyHead.next;
  }

  ListNode* split(ListNode* head, int n) {
    ListNode dummyNode(0);
    auto p = &dummyNode;
    int len = 0;
    auto pre = head;
    while(head && len <= n) {
      if (len == n) { p->next = head;
        pre->next = NULL;
        break;
      }
      pre = head;
      head = head->next;
      len++;
    }
    return dummyNode.next;
  }

  ListNode* sortList(ListNode* head) {
    int len = 0;
    ListNode* p = head;
    while(p) {
      p = p->next;
      len++;
    }

    ListNode dummyHead(0);
    dummyHead.next = head;
    for(int size = 1; size <= len; size <<= 1) {
      auto cur = dummyHead.next;
      auto tail = &dummyHead;
      while (cur) {
        auto left = cur;
        auto right = split(left, size);
        cur = split(right, size);
        tail->next = merge(left, right);
        while(tail->next) tail = tail->next;
      }
    }
    return dummyHead.next;
  }

leetcode[215] Kth Largest Element in an Array 解法及代码

题目描述:

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:

你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kth-largest-element-in-an-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解法1:

最偷懒的解法,直接用stl的部分排序(全排序必然超时), 输出第K个元素


#include <algorithm>

int findKthLargest(vector<int>& nums, int k) {
    std::partial_sort(nums.begin(), nums.begin() + k, nums.end(), [](auto& a, auto& b) {
        return a > b;
    });
    return nums[k - 1];
}

这里需要注意的是partial_sort的第三个参数,需要自定义排序规则,默认从小到大,我们需要从大到小。

解法2:

建立一个大顶堆,使用堆排序,排到第K个数时停止并返回,代码:


void percolateDown(vector<int>& nums, int start, int end) {
    for (int j = 2 * start + 1; j <= end; j = 2 * start + 1) {
        if (j < end && nums[j + 1] >= nums[j]) j++;
        if (nums[start] > nums[j]) break;
        int temp = nums[start];
        nums[start] = nums[j];
        nums[j] = temp;
        start = j;
    }
}

void makeHeap(vector<int>& nums) {
    int len = nums.size();
    for (int r = len / 2 - 1; r >= 0; r--) {
        percolateDown(nums, r, len - 1);
    }
}

int heapSort(vector<int>& nums, int k) {
    int n = nums.size() - 1;
    for (int i = 0; i < k; i++) {
        int temp = nums[n];
        nums[n] = nums[0];
        nums[0] = temp;
        n--;
        percolateDown(nums, 0, n);
    }
    return nums[nums.size() - k];
}

int findKthLargest(vector<int>& nums, int k) {
    makeHeap(nums);
    return heapSort(nums, k);
}