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];
}