这几天在复习毛概,所以稍稍推迟了一下 ,今天继续复习算法
两两交换链表中的节点
原题
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例 1:
输入:head = [1,2,3,4]
输出:[2,1,4,3]
示例 2:输入:head = []
输出:[]
示例 3:输入:head = [1]
输出:[1]
提示:
- 链表中节点的数目在范围
[0, 100]
内 0 <= Node.val <= 100
思路
方法一:递归
如果链表中至少有两个节点,则在两两交换链表中的节点之后,原始链表的头节点变成新的链表的第二个节点,原始链表的第二个节点变成新的链表的头节点。链表中的其余节点的两两交换可以递归地实现。
方法二:迭代
创建哑结点 dummyHead,令 dummyHead.next = head。令 temp 表示当前到达的节点,初始时 temp = dummyHead。每次需要交换 temp 后面的两个节点。
具体而言,交换之前的节点关系是 temp -> node1 -> node2,交换之后的节点关系要变成 temp -> node2 -> node1,因此需要进行如下操作。
temp.next = node2
node1.next = node2.next
node2.next = node1
代码实例
//方法一:递归
struct ListNode* swapPairs(struct ListNode* head) {
if (head == NULL || head->next == NULL) {
return head;
}
struct ListNode* newHead = head->next; //构造一个新的头结点
head->next = swapPairs(newHead->next); //递归循环操作
newHead->next = head;
return newHead;
}
//方法二:迭代
struct ListNode* swapPairs(struct ListNode* head) {
struct ListNode dummyHead;//创建哑结点
dummyHead.next = head;
struct ListNode* temp = &dummyHead;
while (temp->next != NULL && temp->next->next != NULL) {
struct ListNode* node1 = temp->next;
struct ListNode* node2 = temp->next->next;
//交换节点
temp->next = node2;
node1->next = node2->next;
node2->next = node1;
temp = node1;
}
return dummyHead.next;
}
链表的中间结点
原题
给你单链表的头结点 head ,请你找出并返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:head = [1,2,3,4,5]
输出:[3,4,5]
解释:链表只有一个中间结点,值为 3 。
示例 2:
输入:head = [1,2,3,4,5,6]
输出:[4,5,6]
解释:该链表有两个中间结点,值分别为 3 和 4 ,返回第二个结点。
提示:
链表的结点数范围是 [1, 100]
1 <= Node.val <= 100
思路
利用快慢指针,快指针每次走两步,慢指针每次走一步,所以快指针走的距离为慢指针的两倍,故当快指针遍历到链表末尾时,慢指针指向记为中间节点
代码实例
//利用快慢指针
struct ListNode* middleNode(struct ListNode* head){
struct ListNode* f = head;//快指针
struct ListNode* s = head;//慢指针
while(f->next!= NULL && f->next->next!=NULL)
{
f = f->next->next;
s = s->next;
}
if(f->next!= NULL)
s = s->next;
return s;
}
回文链表
原题
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
示例 1:
输入:head = [1,2,2,1]
输出:true
示例 2:
输入:head = [1,2]
输出:false
提示:
链表中节点数目在范围[1, 105] 内
0 <= Node.val <= 9
思路
方法一:将值复制到数组中后用双指针法
- 复制链表值到数组列表中。
- 使用双指针法判断是否为回文。
比较的是节点值的大小,而不是节点本身。正确的比较方式是:
node_1.val == node_2.val
,而node_1 == node_2
是错误的。
方法二:递归
递归为我们提供了一种优雅的方式来方向遍历节点。
function print_values_in_reverse(ListNode head)
if head is NOT null
print_values_in_reverse(head.next)
print head.val
如果使用递归反向迭代节点,同时使用递归函数外的变量向前迭代,就可以判断链表是否为回文。
代码实例
//方法1
bool isPalindrome(struct ListNode* head) {
int vals[50001], vals_num = 0;
while (head != NULL) {
vals[vals_num++] = head->val;
head = head->next;
}
for (int i = 0, j = vals_num - 1; i < j; ++i, --j) {
if (vals[i] != vals[j]) {
return false;
}
}
return true;
}
//方法2
struct ListNode* frontPointer;
bool recursivelyCheck(struct ListNode* currentNode) {
if (currentNode != NULL) {
if (!recursivelyCheck(currentNode->next)) {
return false;
}
if (currentNode->val != frontPointer->val) {
return false;
}
frontPointer = frontPointer->next;
}
return true;
}
bool isPalindrome(struct ListNode* head) {
frontPointer = head;
return recursivelyCheck(head);
}
重排链表
原题
给定一个单链表 L 的头节点 head ,单链表 L 表示为:
L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
输入:head = [1,2,3,4]
输出:[1,4,2,3]
示例 2:
输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]
提示:
链表的长度范围为 [1, 5 * 104]
1 <= node.val <= 1000
思路
线性表重建,利用线性表存储该链表,然后利用线性表可以下标访问的特点,直接按顺序访问指定元素,重建该链表即可。
代码实例
void reorderList(struct ListNode* head) {
if (head == NULL) {
return;
}
struct ListNode* vec[40001];
struct ListNode* node = head;
int n = 0;
while (node != NULL) {
vec[n++] = node;
node = node->next;
}
int i = 0, j = n - 1;
while (i < j) {
vec[i]->next = vec[j];
i++;
if (i == j) {
break;
}
vec[j]->next = vec[i];
j--;
}
vec[i]->next = NULL;
}