0.理论部分
0.1 线性表的定义
线性表(Linear List)是由n(n >= 0)个相同类型的数据元素a1,a2,…,an 组成的有序序列。即表中除首尾元素外,其它元素有且仅有一个直接前驱和直接后继。首元素仅有一个直接后继,尾元素仅有一个直接前驱。表中数据元素的个数称为表的长度,记为:(a1,a2,…,an)。
0.2 线性表的操作
- 随机存取:获取或设置指定索引处的数据元素值。(支持索引器)
- 插入操作:将数据元素值插入到指定索引处。
- 移除操作:移除线性表指定索引处的数据元素。
- 查找操作:寻找具有特征值域的结点并返回其下标。
- 得到表长:获取线性表中实际包含数据元素的个数。
- 是否为空:判断线性表中是否包含数据元素。
- 清空操作:移除线性表中的所有数据元素。
0.3 顺序存储(顺序表)
0.4 链式存储(链表)
利用指针方式实现的线性表称为链表(单链表、循环链表、双链表)。它不要求逻辑上相邻的数据元素在物理位置上也相邻,即:逻辑结构与物理结构可以相同也可以不相同。
单链表:
循环链表:
双向链表:
学习目标:
- 理解线性表的定义与操作。
- 实现顺序表。
- 实现单链表、循环链表、双向链表。
1. 合并两个有序链表
https://leetcode-cn.com/problems/merge-two-sorted-lists/
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4
思路:
首先判断两个链表是否有空集的情况,一个为空返回另一个,两个都为空返回null。然后因为都是有序链表生成一个新的有序链表,比较大小然后穿插成一个即可。最后直到有一个链表为空,再将另一个链表链到新链表后即可。
C++代码如下:
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(!l1 && !l2)
return NULL;
if(!l1 && l2)
return l2;
if(l1 && !l2)
return l1;
ListNode* head = new ListNode(-1);
ListNode* p = head, *q;
while(l1 && l2){
if(l1->val < l2->val){
q = new ListNode(l1->val);
p->next = q;
p = q;
l1 = l1->next;
}
else{
q = new ListNode(l2->val);
p->next = q;
p = q;
l2 = l2->next;
}
}
if(l1)
l2 = l1;
p->next = l2;
return head->next;
}
};
2. 删除链表的倒数第N个节点
https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:给定的 n 保证是有效的。
进阶:你能尝试使用一趟扫描实现吗?
思路:
使用双指针法。front节点和rear节点之间有n 个节点,并同时向后移动。当rear为NULL时停止窗口移动,并删除front->next节点。时间复杂度为O(n)。
参考:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/solution/c-shuang-zhi-zhen-kuai-su-jie-ti-zhu-shi-by-y36ymh/
C++代码如下:
class Solution{
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* front = head;
ListNode* rear = head;
if(head->next == NULL) return NULL;
while(n-- >= 0){
if(rear == NULL) return head -> next;
rear = rear->next;
// n --;
}
while(rear){
rear = rear -> next;
front = front -> next;
}
front->next = front->next->next;
return head;
}
};
3. 旋转链表
https://leetcode-cn.com/problems/rotate-list/
给定一个链表,旋转链表,将链表每个节点向右移动k个位置,其中k是非负数。
示例 1:
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:
输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL
思路:
移动元素k位,相对于移动head(N - k % N)位,且新链表的尾结点在该新head的前一位(N - k % N - 1)。
首先,记录结点个数,并且做成环链表;
然后,计算N - k % N,这个head移动该步数后,新head所在的位置;
最后,移动head(N - k % N - 1)位,得到新链表的尾指针,该指针的下一位即新表的头结点。
参考:https://leetcode-cn.com/problems/rotate-list/solution/yuan-de-xiang-dui-yi-dong-zhe-yang-jie-shi-by-leaf/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(head == NULL) return head;//指针为空,直接返回
int N = 1;
ListNode* tail = head;
while(tail -> next) {
tail = tail -> next;
N++;
}
tail -> next = head; // 首尾相连,做环
k = N - k % N; // head的相对移动数(注意这个地方的理解)
// 对head少移动1位,得到新表尾指针的位置
while(k - 1) {
head = head -> next;
k--;
}
tail = head;
head = head -> next;
tail -> next = NULL;
return head;//返回新链表
}
};