目录
1、轮转数组
2、返回倒数第k个节点
3、判断链表的回文结构
4、两个链表,找出它们的第一个公共结点
5、随机链表的复制
6、判断链表中是否有环
7、返回链表开始入环的第一个节点
1、轮转数组
给定一个整数数组 nums
,将数组中的元素向右轮转 k
个位置,其中 k
是非负数
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3 输出:[5,6,7,1,2,3,4]
解释: 向右轮转 1 步:[7,1,2,3,4,5,6]
向右轮转 2 步:[6,7,1,2,3,4,5]
向右轮转 3 步:[5,6,7,1,2,3,4]
思路:
先逆置后k个元素
再逆置前n-k个元素
最后整体逆置
void Reverse(int* nums,int a,int b)
{
int k = (b-a+1)/2;
while(k--)
{
int tmp = nums[a];
nums[a] = nums[b];
nums[b] = tmp;
++a;
--b;
}
}
void rotate(int* nums, int numsSize, int k)
{
k = k % numsSize;//逆置numSize次不变
Reverse(nums,numsSize-k,numsSize-1);
Reverse(nums,0,numsSize-k-1);
Reverse(nums,0,numsSize-1);
}
2、返回倒数第k个节点
找出单向链表中倒数第 k 个节点,返回该节点的值
示例:
输入: 1->2->3->4->5 和 k = 2 输出: 4
说明:
给定的 k 保证是有效的
思路1:
先遍历链表,算出节点个数count
再遍历count-k次,找到倒数第k个节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
int kthToLast(struct ListNode* head, int k)
{
struct ListNode* cur = head;
int count = 0;
while(cur)
{
count++;
cur = cur->next;
}
cur = head;
int n = count-k;
while(n--)
{
cur = cur->next;
}
return cur->val;
}
思路2:
快慢指针 fast slow
fast指针先走k步
fast和slow一起走
直到fast为NULL,slow指向倒数第k个节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
int kthToLast(struct ListNode* head, int k)
{
struct ListNode* fast = head;
struct ListNode* slow = head;
while(k--)
{
fast = fast->next;
}
while(fast)
{
slow = slow->next;
fast = fast->next;
}
return slow->val;
}
3、判断链表的回文结构
描述:
对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针head,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。
测试样例:
1->2->2->1
返回:true
思路:
找中间节点,偶数后一个
逆置后半部分的链表
判断相等
//找中间节点
struct ListNode* MiddleNode(struct ListNode* head) {
struct ListNode* fast = head;
struct ListNode* slow = head;
//注意两种情况用whlie(&&),我看了几小时,已废
while (fast != NULL && fast->next != NULL) {
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
//逆置链表
struct ListNode* ReverseList(struct ListNode* head) {
struct ListNode* cur = head;
if (cur == NULL) {
return head;
}
struct ListNode* next = head->next;
if (next == NULL) {
return head;
}
while (next) {
struct ListNode* tail = next->next;
next->next = cur;
cur = next;
next = tail;
}
head->next = NULL;
return cur;
}
bool test(ListNode* head)
{
struct ListNode* mid = MiddleNode(head);
struct ListNode* head1 = ReverseList(mid);
while (head1 != NULL) {
if (head->val != head1->val)
return false;
else {
head = head->next;
head1 = head1->next;
}
}
return true;
}
4、两个链表,找出它们的第一个公共结点
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的第一个起始节点。如果两个链表不存在相交节点,返回 NULL
整个链式结构中不存在环
图示两个链表在节点 c1
开始相交:
思路1:
双重嵌套遍历
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
while(headA)
{
struct ListNode* s = headB;
while(s)
{
if(headA == s)
{
return headA;
}
else
{
s = s->next;
}
}
headA = headA->next;
}
return NULL;
}
思路二:
先遍历连个链表,k为节点差值
LongList先走k步
再LongList和ShortList一起走
若LongList == ShortList,即为共同节点,否则返回NULL
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* getIntersectionNode(struct ListNode* headA,struct ListNode* headB)
{
struct ListNode* A = headA;
struct ListNode* B = headB;
int countA = 0;
int countB = 0;
while (A)
{
countA++;
A = A->next;
}
while (B)
{
countB++;
B = B->next;
}
struct ListNode* LongList = headA, *ShortList = headB;
if(countA<countB)
{
LongList = headB;
ShortList = headA;
}
int k = abs(countA - countB);
while(k--)
{
LongList = LongList->next;
}
while(LongList)
{
if(LongList == ShortList)
{
return LongList;
}
else
{
LongList = LongList->next;
ShortList = ShortList->next;
}
}
return NULL;
}
5、随机链表的复制
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点
要求返回这个链表的深度拷贝
即建立一个新链表与原链表一模一样,且复制链表中的指针都不应指向原链表中的节点
示例 1:
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]] 输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
注意:[13,0],0表示指向下标为0的节点
思路:
原链表:A --> B --> C --> D
先复制值 A --> A' --> B --> B' --> C --> C' --> D --> D'
再复制random(以A'举例) A->next->random = A->random->next;(A->random = NULL,另做讨论)
最后连接复制链表 A' --> B' --> C' --> D'
/**
* Definition for a Node.
* struct Node {
* int val;
* struct Node *next;
* struct Node *random;
* };
*/
struct Node* copyRandomList(struct Node* head) {
struct Node* cur = head;
if(cur == NULL)
{
return cur;
}
else
{
//复制值
while(cur)
{
struct Node* Node = (struct Node*)malloc(sizeof(struct Node));
Node->val = cur->val;
Node->next = cur->next;
cur->next = Node;
cur = cur->next->next;
}
//复制random
cur = head;
while(cur)
{
if(cur->random == NULL)
{
cur->next->random = NULL;
}
else
{
cur->next->random = cur->random->next;
}
cur = cur->next->next;
}
//连接复制链表
cur = head;
struct Node* copyhead = head->next;
struct Node* copytail = head->next;
while(cur)
{
struct Node* next = copytail->next;//先保留原链表节点
if(next == NULL)
{
cur->next = next;//还原原链表
cur = next;
}
else
{
copytail->next = copytail->next->next;
copytail = copytail->next;
cur->next = next;//还原原链表
cur = next;
}
}
return copyhead;
}
}
6、判断链表中是否有环
如果链表中存在环 ,则返回 true
。 否则,返回 false
示例 1:
pos表示节点的下标
输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点
思路:
快慢指针,fast slow
fast = fast->next->next;
slow = slow->next;
若为环一定能相遇,返回true,否则返回false
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head)
{
struct ListNode* fast = head;
struct ListNode* slow = head;
while(fast&&fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
{
return true;
}
}
return false;
}
7、返回链表开始入环的第一个节点
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL
示例 1:
pos表示节点的下标
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。
思路1:
快慢指针 fast slow
fast = fast->next->next;
slow = slow->next;
L为head到入环点的距离,C为环的长度,N为入环点到meet的距离
slow走的路程:L+N
fast走的路程:L+x*C+N
由于2*slow = fast
所以 L = x*C-N,x>=1,因为L不会和-N相等
当x为1时,L = C-N,meet与head相遇点即为入环点
L = x*C-N 也可以写成 L = (x-1)C+C-N,head一直在走,meet先转x-1圈,再走C-N与head相遇
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *detectCycle(struct ListNode *head)
{
struct ListNode* fast = head;
struct ListNode* slow = head;
//找meet
while(fast&&fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
{
//有环,一定有入环点
struct ListNode* meet = slow;
//meet与head一起走
while(1)
{
if(meet == head)
{
return meet;
}
else
{
meet = meet->next;
head = head->next;
}
}
}
}
return NULL;
}
思路2:
newhead = meet->next;
meet->next = NULL;
这样就成链表相交问题了
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
//找链表相交节点
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
while(headA)
{
struct ListNode* s = headB;
while(s)
{
if(headA == s)
{
return headA;
}
else
{
s = s->next;
}
}
headA = headA->next;
}
return NULL;
}
struct ListNode *detectCycle(struct ListNode *head)
{
struct ListNode* fast = head;
struct ListNode* slow = head;
//找meet
while(fast&&fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
{
struct ListNode* meet = slow;
struct ListNode* newhead = meet->next;
meet->next = NULL;
struct ListNode* s = getIntersectionNode(head,newhead);
meet->next = newhead;
return s;
}
}
return NULL;
}