链表
注:以下链表均为不带头结点的单链表
- 删除链表中的节点
请编写一个函数,用于 删除单链表中某个特定节点 。在设计函数时需要注意,你无法访问链表的头节点 head ,只能直接访问 要被删除的节点 。
题目数据保证需要删除的节点 不是末尾节点 。
法一:值删除
void deleteNode(ListNode* node) {
int left = node->val;
ListNode* temp = node->next;
for ( ; temp->next != NULL; temp = temp->next)
{
node->val = temp->val; //值拷贝
node = node->next; //下一个
}
node->val = temp->val;
node->next = NULL;
}
题解思路:因题目要求无法访问头节点,故无法访问其前驱结点,因此使用值拷贝的方式,进行“删除”。类似于数组元素的删除。
法二:删除“无辜”结点
void deleteNode(ListNode* node) {
node->val = node->next->val;
node->next = node->next->next;
}
题解思路:因题目要求无法访问头节点,故无法访问其前驱结点,因此可以将其后继元素值拷贝至此结点,再将其后继元素删除即可。此举比法一更简单有效。
- 删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第
n
个结点,并且返回链表的头结点。
ListNode* removeNthFromEnd(ListNode* head, int n)
{
ListNode* slow = head; //慢指针
ListNode* fast = head; //快指针
for (int i = 0; i < n; i++)
fast = fast->next; //保证slow和fast之间含有n - 1个元素
if (fast == NULL)
return head->next; //删除第一个结点
while (fast->next != NULL) //fast为末尾元素
{
slow = slow->next;
fast = fast->next;
}
//此时fast指向末尾,slow指向待删除元素的前驱
slow->next = slow->next->next;
return head;
}
题解思路:双指针,使右指针指向末尾结点,左指针指向待删除结点的前驱,即保持两者之间相差n - 1个节点即可,然后左指针进行跨越指向,即可删除。
注意是否删除头节点的情况。
- 反转链表
给你单链表的头节点
head
,请你反转链表,并返回反转后的链表。
ListNode* reverseList(ListNode* head) {
if (head == NULL) return head; //空链
//ListNode* node = head->next;
//cout << node->val << endl;
head = reveseList_recursion(head);
return head;
}
ListNode* reveseList_recursion(ListNode* node) //递归
{
ListNode* temp = NULL;
if (node->next != NULL) //直至末尾
temp = reveseList_recursion(node->next);
else
return node; //抵达末尾
//temp->next = node;
ListNode* tmp = temp;
while (tmp->next != NULL)
{
tmp = tmp->next;
}
node->next = NULL;
tmp->next = node; //反向指向
return temp;
}
题解思路:递归思想,先使得结点遍历至尾结点,然后反向指向即可。
- 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
if (list1 == NULL|| list2 == NULL)
return list1 == NULL ? list2 : list1;
ListNode* result = (ListNode*)malloc(sizeof(ListNode));
if (result == NULL)
exit(0);
ListNode* temp = result;
while (list1 != NULL || list2 != NULL)
{
if (list1 == NULL || list2==NULL)
{
temp->next = list1 == NULL ? list2 : list1;
break;
}
temp->next = list1->val > list2->val ? list2 : list1;
temp = temp->next;
if (list1->val > list2->val)
list2 = list2->next; //向后遍历
else
list1 = list1->next;
}
return result->next;
}
题解思路:首先判断两者是否为空表的情况,然后依次遍历两个表直至都遍历完,其中结点值小的先链入新表中,当相对较短的链接抵达尾结点后将另外一个链表直接链入新链表即可
- 回文链表
给你一个单链表的头节点
head
,请你判断该链表是否为回文链表。如果是,返回true
;否则,返回false
。
bool isPalindrome(ListNode* head)
{
vector<int> ar;
while (head != NULL)
{
ar.push_back(head->val);
head = head->next;
}
int left = 0, right = ar.size() - 1;
while (left < right)
{
if (ar[left++] != ar[right--])
return false;
}
return true;
}
题解思路:将其所有结点值依次存入数组中,判断数组是否为回文数组即可
- 环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
bool hasCycle(ListNode* head) {
ListNode* fast = head;
ListNode* slow = head;
while (fast != NULL && fast->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
if (fast == slow)
return true;
}
return false;
}
题解思路:环形问题,直接使用快慢指针即可,由于fast始终比slow多走一步,因此若有环,则两者肯定在某一个结点处相遇
2022年5月26日