update string
This commit is contained in:
@@ -10,6 +10,14 @@
|
||||
* 链表中是否有环
|
||||
|
||||
|
||||
链表结点定义如下:
|
||||
struct ListNode
|
||||
{
|
||||
int m_nKey;
|
||||
ListNode* m_pNext;
|
||||
};
|
||||
|
||||
|
||||
|
||||
常用解题思路
|
||||
|
||||
@@ -19,16 +27,34 @@
|
||||
|
||||
|
||||
|
||||
## 输出链表中倒数第k个结点
|
||||
## 输出链表中倒数第k个节点
|
||||
|
||||
题目:输入一个单向链表,输出该链表中倒数第k个结点。链表的倒数第0个结点为链表的尾指针。
|
||||
|
||||
链表结点定义如下:
|
||||
struct ListNode
|
||||
{
|
||||
int m_nKey;
|
||||
ListNode* m_pNext;
|
||||
};
|
||||
思路一:2遍遍历,先遍历一遍链表算出 n 的值,然后再遍历链表计算第 n - k 个节点
|
||||
思路二:双指针,一个指针先走k步,然后 两个指针一起同步往前走,前面先走的指针到链表尾部吧,后面的指针刚好是第k个节点
|
||||
|
||||
|
||||
类似的题目还有
|
||||
|
||||
`删除第k个节点`
|
||||
`链表的中间结点` : 使用「快慢指针」的技巧 ,慢指针 slow 前进一步,快指针 fast 就前进两步,这样,当 fast 走到链表末尾时,slow 就指向了链表中点。
|
||||
|
||||
|
||||
|
||||
## 给定单链表,检测是否有环。
|
||||
|
||||
```
|
||||
int isLinkCicle(Link *head);
|
||||
```
|
||||
|
||||
解题思路
|
||||
|
||||
1. 暴力
|
||||
2. 空间换时间,用一个HashMap
|
||||
3. 快慢指针: 使用两个指针p1,p2从链表头开始遍历,p1每次前进一步,p2每次前进两步。如果p2到达链表尾部,说明无环,否则p1、p2必然会在某个时刻相遇(p1==p2),从而检测到链表中有环。
|
||||
|
||||
|
||||
|
||||
|
||||
## 链表反转
|
||||
@@ -41,13 +67,6 @@ struct ListNode
|
||||
|
||||
输入一个链表的头结点,从尾到头反过来输出每个结点的值。链表结点定义如下:
|
||||
|
||||
```
|
||||
struct ListNode{
|
||||
int m_nKey;
|
||||
ListNode* m_pNext;
|
||||
};
|
||||
```
|
||||
|
||||
思路一: 辅助栈,需要一个栈空间
|
||||
思路二: 反转链表,然后遍历
|
||||
思路三: 递归实现,将printf语句放在递归调用后面。果然妙极。。
|
||||
|
||||
@@ -25,19 +25,6 @@ Node * sort_link_list_increasing_order (Node *pheader):
|
||||
|
||||
|
||||
|
||||
## 给定单链表,检测是否有环。
|
||||
|
||||
```
|
||||
int isLinkCicle(Link *head);
|
||||
```
|
||||
|
||||
解题思路
|
||||
|
||||
1. 暴力
|
||||
2. 空间换时间,用一个HashMap
|
||||
3. 快慢指针: 使用两个指针p1,p2从链表头开始遍历,p1每次前进一步,p2每次前进两步。如果p2到达链表尾部,说明无环,否则p1、p2必然会在某个时刻相遇(p1==p2),从而检测到链表中有环。
|
||||
|
||||
|
||||
|
||||
类似的题有:
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
# 链表-2条
|
||||
|
||||
|
||||
## 找出两个链表的第一个公共结点
|
||||
|
||||
题目:两个单向链表,找出它们的第一个公共结点。链表的结点定义为:
|
||||
链表的结点定义为:
|
||||
```
|
||||
struct ListNode
|
||||
{
|
||||
@@ -12,13 +9,39 @@ struct ListNode
|
||||
};
|
||||
```
|
||||
|
||||
分析:第一个公共节点,也就是2个链表中的节点的m_pNext 指向的同一个节点。
|
||||
|
||||
## 找出两个链表的第一个公共结点
|
||||
|
||||
题目:两个单向链表,找出它们的第一个公共结点。
|
||||
|
||||
分析:第一个公共节点,也就是2个链表中的节点的m_pNext 指向的同一个节点。 2遍遍历方法:
|
||||
|
||||
1. 先遍历2个链表,得到各自的长度,和差sub
|
||||
2. 长链表先遍历sub个节点,然后2个节点一起遍历
|
||||
3. 第一次同时指向的同一个节点就是这个commonNode
|
||||
|
||||
|
||||
有没有可能一遍遍历就解决问题呢?
|
||||
|
||||
让 p1 遍历完链表 A 之后开始遍历链表 B,让 p2 遍历完链表 B 之后开始遍历链表 A,这样相当于「逻辑上」两条链表接在了一起。
|
||||
空间复杂度为 O(1),时间复杂度为 O(N), 一遍遍历就搞定
|
||||
|
||||
```
|
||||
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
|
||||
// p1 指向 A 链表头结点,p2 指向 B 链表头结点
|
||||
ListNode p1 = headA, p2 = headB;
|
||||
while (p1 != p2) {
|
||||
// p1 走一步,如果走到 A 链表末尾,转到 B 链表
|
||||
if (p1 == null) p1 = headB;
|
||||
else p1 = p1.next;
|
||||
// p2 走一步,如果走到 B 链表末尾,转到 A 链表
|
||||
if (p2 == null) p2 = headA;
|
||||
else p2 = p2.next;
|
||||
}
|
||||
return p1;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 判断俩个链表是否相交
|
||||
@@ -32,6 +55,81 @@ struct ListNode
|
||||
|
||||
|
||||
|
||||
## 合并2条有序链表
|
||||
|
||||
while 循环每次比较 p1 和 p2 的大小,把较小的节点接到结果链表上
|
||||
|
||||
```
|
||||
ListNode mergeTwoLists(ListNode l1, ListNode l2) {
|
||||
// 虚拟头结点
|
||||
ListNode dummy = new ListNode(-1), p = dummy;
|
||||
ListNode p1 = l1, p2 = l2;
|
||||
|
||||
while (p1 != null && p2 != null) {
|
||||
// 比较 p1 和 p2 两个指针
|
||||
// 将值较小的的节点接到 p 指针
|
||||
if (p1.val > p2.val) {
|
||||
p.next = p2;
|
||||
p2 = p2.next;
|
||||
} else {
|
||||
p.next = p1;
|
||||
p1 = p1.next;
|
||||
}
|
||||
// p 指针不断前进
|
||||
p = p.next;
|
||||
}
|
||||
|
||||
if (p1 != null) {
|
||||
p.next = p1;
|
||||
}
|
||||
|
||||
if (p2 != null) {
|
||||
p.next = p2;
|
||||
}
|
||||
|
||||
return dummy.next;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 合并k个有序链表
|
||||
|
||||
|
||||
相比合并两个有序链表,难点在于,如何快速得到 k 个节点中的最小节点,接到结果链表上?
|
||||
|
||||
就要用到 优先级队列(二叉堆) 这种数据结构,把链表节点放入一个最小堆,就可以每次获得 k 个节点中的最小节点:
|
||||
|
||||
|
||||
```
|
||||
ListNode mergeKLists(ListNode[] lists) {
|
||||
if (lists.length == 0) return null;
|
||||
// 虚拟头结点
|
||||
ListNode dummy = new ListNode(-1);
|
||||
ListNode p = dummy;
|
||||
// 优先级队列,最小堆
|
||||
PriorityQueue<ListNode> pq = new PriorityQueue<>(
|
||||
lists.length, (a, b)->(a.val - b.val));
|
||||
// 将 k 个链表的头结点加入最小堆
|
||||
for (ListNode head : lists) {
|
||||
if (head != null)
|
||||
pq.add(head);
|
||||
}
|
||||
|
||||
while (!pq.isEmpty()) {
|
||||
// 获取最小节点,接到结果链表中
|
||||
ListNode node = pq.poll();
|
||||
p.next = node;
|
||||
if (node.next != null) {
|
||||
pq.add(node.next);
|
||||
}
|
||||
// p 指针不断前进
|
||||
p = p.next;
|
||||
}
|
||||
return dummy.next;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 输出两个非降序链表的并集
|
||||
|
||||
请修改append函数,利用这个函数实现:
|
||||
@@ -39,3 +137,11 @@ struct ListNode
|
||||
两个非降序链表的并集,1->2->3 和 2->3->5 并为 1->2->3->5 。另外只能输出结果,不能修改两个链表的数据。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -32,11 +32,53 @@
|
||||
|
||||
输入n,用最快的方法求该数列的第n项。
|
||||
|
||||
虽然fibonacci数列是`递归`的经典应用,但递归效率很差,会有很多重复的计算,复杂度是成指数递增的,我测试了下计算50的时候已经要300s了。
|
||||
思路一:`递归`,虽然fibonacci数列是`递归`的经典应用,但递归效率很差,会有很多重复的计算,复杂度是成指数递增的,我测试了下计算50的时候已经要300s了。
|
||||
|
||||
```
|
||||
int fib(int N) {
|
||||
if (N == 1 || N == 2) return 1;
|
||||
return fib(N - 1) + fib(N - 2);
|
||||
}
|
||||
```
|
||||
|
||||
思路二:从下往上计算,复杂度O(N),一个循环就搞定
|
||||
|
||||
思路三: logN
|
||||
```
|
||||
public int fib(int n){
|
||||
if (n == 0) return 0;
|
||||
|
||||
//缓存
|
||||
int[] dp = new int[n + 1];
|
||||
|
||||
// base case
|
||||
dp[0] = 0; dp[1] = 1;
|
||||
|
||||
// 状态转移
|
||||
for (int i = 2; i <= n; i++) {
|
||||
dp[i] = dp[i - 1] + dp[i - 2];
|
||||
}
|
||||
|
||||
return dp[n];
|
||||
}
|
||||
```
|
||||
|
||||
说一个细节优化点,当前状态只和之前的两个状态有关,其实并不需要那么长的一个 DP table 来存储所有的状态,只要想办法存储之前的两个状态就行了。所以,可以进一步优化,把空间复杂度降为 O(1):
|
||||
|
||||
```
|
||||
int fib(int n) {
|
||||
if (n < 1) return 0;
|
||||
if (n == 2 || n == 1)
|
||||
return 1;
|
||||
int prev = 1, curr = 1;
|
||||
for (int i = 3; i <= n; i++) {
|
||||
int sum = prev + curr;
|
||||
prev = curr;
|
||||
curr = sum;
|
||||
}
|
||||
return curr;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user