目录
1.牛客BM3 链表中的节点每k个一组翻转
2.BM4 合并两个排序的链表
3.BM5 合并k个已排序的链表
4.BM6 判断链表中是否有环
5.BM7 链表中环的入口结点
6.BM8 链表中倒数最后k个结点
7.BM9 删除链表的倒数第n个节点
8.BM10 两个链表的第一个公共结点
9.BM11 链表相加(二)
10.BM12 单链表的排序
1.牛客BM3 链表中的节点每k个一组翻转
题目:将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表,如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样,你不能更改节点中的值,只能更改节点本身。
数据范围: \ 0 \le n \le 2000 0≤n≤2000 , 1 \le k \le 20001≤k≤2000 ,链表中每个元素都满足 0 \le val \le 10000≤val≤1000
要求空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
例如:
给定的链表是 1\to2\to3\to4\to51→2→3→4→5
对于 k = 2k=2 , 你应该返回 2\to 1\to 4\to 3\to 52→1→4→3→5
对于 k = 3k=3 , 你应该返回 3\to2 \to1 \to 4\to 53→2→1→4→5
解:这道题用递归做效率是最高的,但不好理解。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param head ListNode类
* @param k int整型
* @return ListNode类
*/
struct ListNode*reverse (struct ListNode*phead,int k)
{
struct ListNode*pre ,*cur ,*nxt;//创建3个新结点
pre=NULL;cur=phead;nxt=phead->next;//pre为头结点
while (k--) //在k区间内反转
{
cur->next=pre;
pre=cur;
cur=nxt;
nxt=cur->next;
}
return pre;//返回反转后的头结点
}
struct ListNode* reverseKGroup(struct ListNode* head, int k ) {
// write code here
struct ListNode* reverseHead=(struct ListNode*)malloc(sizeof(struct ListNode));
reverseHead->next=head;
struct ListNode *revHead,*revEnd;
int count=0;
revHead=reverseHead,revEnd=reverseHead->next;
if(k==1)return reverseHead->next;
while(revEnd!=NULL)
{
revEnd=revEnd->next;
if(++count==k)//计数count等于k则开始反转
{
revHead->next=reverse(revHead->next, k);
while(count--)
{
revHead=revHead->next;
}
revHead->next=revEnd;
count=0;
}
}
return reverseHead->next;
}
2.BM4 合并两个排序的链表
题目:输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
数据范围: 0 \le n \le 10000≤n≤1000,-1000 \le 节点值 \le 1000−1000≤节点值≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
如输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6},所以对应的输出为{1,2,3,4,5,6}
解:这道题和第一道比就比较简单了,直接笨想用两个指针分别指向两个链表然后比较大小,返回到新链中就可以了。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
*
* @param pHead1 ListNode类
* @param pHead2 ListNode类
* @return ListNode类
*/
struct ListNode* Merge(struct ListNode* pHead1, struct ListNode* pHead2 ) {
// write code here
struct ListNode*p1=pHead1,*p2=pHead2,*res;
struct ListNode*cur=(struct ListNode*)malloc(sizeof(struct ListNode));
cur->val=-1;
res=cur;
while ((p1!=NULL&&p2!=NULL)) {
if (p1->val<p2->val) {
res->next=p1;
res=res->next;
p1=p1->next;
}
else {
res->next=p2;
res=res->next;
p2=p2->next;
}
}
if ((p1!=NULL)) {
res->next=p1;
}
if ((p2!=NULL)) {
res->next=p2;
}
return cur->next;
}
向上面那么做,理解起来非常容易,但也是入门的方法;
人写迭代,神写递归,接下来看看递归的程序怎么做
这道题给的函数功能就是合并两个排序的链表,那我们可以用这个函数本身来递归。
(恍然大悟,原来如此啊!迭代看着递归,表情惊恐:这就是神吗?竟恐怖如斯,自己递归自己。 )呵呵呵呵,冷笑话~这道题其实递归和迭代时间复杂度是一样的,空间复杂度还不如迭代。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
*
* @param pHead1 ListNode类
* @param pHead2 ListNode类
* @return ListNode类
*/
struct ListNode* Merge(struct ListNode* pHead1, struct ListNode* pHead2 ) {
// write code here
if (!pHead1) return pHead2;
if (!pHead2) return pHead1;
if (pHead1->val <= pHead2->val) {
pHead1->next = Merge(pHead1->next, pHead2);
return pHead1;
}
else {
pHead2->next = Merge(pHead1, pHead2->next);
return pHead2;
}
}
3.BM5 合并k个已排序的链表
题目:合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。
数据范围:节点总数 0 \le n \le 50000≤n≤5000,每个节点的val满足 |val| <= 1000∣val∣<=1000
要求:时间复杂度 O(nlogn)O(nlogn)
示例1
输入:
[{1,2,3},{4,5,6,7}]
复制返回值:
{1,2,3,4,5,6,7}
解:一样的做法,但是复杂了好多, free函数释放内存空间
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
*
* @param lists ListNode类一维数组
* @param listsLen int lists数组长度
* @return ListNode类
*/
struct ListNode* merger(struct ListNode** lists,int listLen,int left,int right){
if(left == right) {
return lists[left];
}else{
int mid = left + (right - left) / 2;
struct ListNode* leftHead = merger(lists,listLen,left,mid);
struct ListNode* rightHead = merger(lists,listLen,mid + 1,right);
struct ListNode* newHead = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* pre = newHead, *p1 = leftHead, *p2 = rightHead;
while(p1 && p2){
if(p1->val < p2->val){
pre->next = p1;
pre = p1;
p1 = p1->next;
}else{
pre->next = p2;
pre = p2;
p2 = p2->next;
}
}
if(p1){
pre->next = p1;
}else if(p2){
pre->next = p2;
}
pre = newHead->next;
free(newHead);
return pre;
}
}
struct ListNode* mergeKLists(struct ListNode** lists, int listsLen ) {
// write code here
if(listsLen == 0) return NULL;
return merger(lists,listsLen,0,listsLen - 1);
}
4.BM6 判断链表中是否有环
题目:判断给定的链表中是否有环。如果有环则返回true,否则返回false。
数据范围:链表长度 0 \le n \le 100000≤n≤10000,链表中任意节点的值满足 |val| <= 100000∣val∣<=100000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
输入分为两部分,第一部分为链表,第二部分代表是否有环,然后将组成的head头结点传入到函数里面。-1代表无环,其它的数字代表有环,这些参数解释仅仅是为了方便读者自测调试。实际在编程时读入的是链表的头节点。
解:这道题超简单,用双指针一快一慢循环向前,相遇的时候就是有环。
#include <stdbool.h>
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
*
* @param head ListNode类
* @return bool布尔型
*/
bool hasCycle(struct ListNode* head ) {
// write code here
struct ListNode *fast,*slow;
fast=head;
slow=head;
while(fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)
{
return true;
}
}
return false;
}
5.BM7 链表中环的入口结点
题目:给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
数据范围: n\le10000n≤10000,1<=结点值<=100001<=结点值<=10000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
解:大风车呀~嘎吱,嘎吱的转~转就完了,兄弟们!
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pHead ListNode类
* @return ListNode类
*/
struct ListNode* EntryNodeOfLoop(struct ListNode* pHead ) {
// write code here
struct ListNode *fast,*slow;
fast=pHead;
slow=pHead;
while(fast!=NULL&&fast->next!=NULL)
{
fast=fast->next->next;
slow=slow->next;
if(fast==slow)break;
}
if(fast==NULL||fast->next==NULL)return NULL;
fast=pHead;//重新指向头结点
while(fast!=slow)
{
fast=fast->next;
slow=slow->next;
}
return fast;
}
6.BM8 链表中倒数最后k个结点
题目:输入一个长度为 n 的链表,设链表中的元素的值为 ai ,返回该链表中倒数第k个节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。
数据范围:0 \leq n \leq 10^50≤n≤105,0 \leq a_i \leq 10^90≤ai≤109,0 \leq k \leq 10^90≤k≤109
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)
进阶:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
示例1
输入:
{1,2,3,4,5},2
复制返回值:
{4,5}
复制说明:返回倒数第2个节点4,系统会打印后面所有的节点来比较。
解:还是用双指针。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pHead ListNode类
* @param k int整型
* @return ListNode类
*/
struct ListNode* FindKthToTail(struct ListNode* pHead, int k ) {
// write code here
if(pHead==NULL)return NULL;
struct ListNode*a,*b;
a=b=pHead;
int i=0;
while(a!=NULL)
{
if(i<k)
{
i++;
}
else if(i==k)
{
b=b->next;
}
a=a->next;
}
if(i==k)
{
return b;
}
return NULL;
}
7.BM9 删除链表的倒数第n个节点
题目:给定一个链表,删除链表的倒数第 n 个节点并返回链表的头指针
例如,
给出的链表为: 1\to 2\to 3\to 4\to 51→2→3→4→5, n= 2n=2.
删除了链表的倒数第 nn 个节点之后,链表变为1\to 2\to 3\to 51→2→3→5.
数据范围: 链表长度 0\le n \le 10000≤n≤1000,链表中任意节点的值满足 0 \le val \le 1000≤val≤100
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
备注:题目保证 nn 一定是有效的
解:这道题本质上和上到题区别不大;
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param head ListNode类
* @param n int整型
* @return ListNode类
*/
struct ListNode* removeNthFromEnd(struct ListNode* head, int n ) {
// write code here
if(head==NULL)return NULL;
struct ListNode*fast,*slow;
fast=slow=head;
for(int i=0;i<n;i++)
{
fast=fast->next;
}
if(fast==NULL)return head->next;
while(fast->next!=NULL)
{
fast=fast->next;
slow=slow->next;
}
slow->next=slow->next->next;
return head;
}
8.BM10 两个链表的第一个公共结点
题目:输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
数据范围: n \le 1000n≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
例如,输入{1,2,3},{4,5},{6,7}时,两个无环的单向链表的结构如下图所示:
可以看到它们的第一个公共结点的结点值为6,所以返回结点值为6的结点。
解:这道题用两个指针相同速度移动,移动到空时,在到换链表移动,相等则是公共结点。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
*
* @param pHead1 ListNode类
* @param pHead2 ListNode类
* @return ListNode类
*/
struct ListNode* FindFirstCommonNode(struct ListNode* pHead1, struct ListNode* pHead2 ) {
// write code here
struct ListNode*l1=pHead1,*l2=pHead2;
while(l1!=l2)
{
l1=(l1==NULL)?pHead2:l1->next;
l2=(l2==NULL)?pHead1:l2->next;
}
return l1;
}
9.BM11 链表相加(二)
题目:假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
给定两个这种链表,请生成代表两个整数相加值的结果链表。
数据范围:0 \le n,m \le 10000000≤n,m≤1000000,链表任意值 0 \le val \le 90≤val≤9
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)
例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。
解:这道我们可以先反转链表,然后在相加,进位。很烦
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct mylist{
int cnt;
struct ListNode *list;
};
struct ListNode *swaplist(struct ListNode *head, int *cnt)
{
struct ListNode *cur1=head;
struct ListNode *pre1=NULL, *nxt1=NULL;
int count = 0;
while(cur1)
{
nxt1 = cur1->next;
cur1->next = pre1;
pre1 = cur1;
cur1 = nxt1;
count++;
}
*cnt = count;
return pre1;
}
/**
*
* @param head1 ListNode类
* @param head2 ListNode类
* @return ListNode类
*/
struct ListNode* addInList(struct ListNode* head1, struct ListNode* head2 ) {
// write code here
struct mylist mylist1,mylist2;
char flag = 0;
int oldval=0;
if(NULL == (head1 && head2))
return head1?head1:head2;
//获取到反转后的链表和链表长度,并保存到mylist中
mylist1.list = swaplist(head1, &mylist1.cnt);
mylist2.list = swaplist(head2, &mylist2.cnt);
struct ListNode *pre=NULL, *nxt=NULL;
struct ListNode *larger, *little;
if(mylist1.cnt > mylist2.cnt){
larger = mylist1.list;
little = mylist2.list;
}else{
larger = mylist2.list;
little = mylist1.list;
}
//遍历长链表
while(larger)
{
if(little)//锻炼表没到尾
{
oldval = larger->val;
larger->val = (little->val + oldval + flag)%10;
//进位flag置1 否则清0
flag = (little->val + oldval + flag >= 10) ? 1 : 0;
little = little->next;
}else if(flag){
oldval = larger->val;
larger->val = (oldval + 1)%10;
flag = oldval+1 >= 10? 1 : 0;
}
nxt = larger->next;
larger->next = pre;
pre = larger;
larger = nxt;
}
if(flag){
struct ListNode *newhead = (struct ListNode *)malloc(sizeof(struct ListNode));
newhead->next = pre;
newhead->val = 1;
return newhead;
}else{
return pre;
}
}
10.BM12 单链表的排序
题目:给定一个节点数为n的无序单链表,对其按升序排序。
数据范围:0 < n \le 1000000<n≤100000,保证节点权值在[-10^9,10^9][−109,109]之内。
要求:空间复杂度 O(n)O(n),时间复杂度 O(nlogn)O(nlogn)
示例1
输入:
[1,3,2,4,5]
复制返回值:
{1,2,3,4,5}
解:双指针比较大小排序
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/**
*
* @param head ListNode类 the head node
* @return ListNode类
*/
struct ListNode* sortInList(struct ListNode* head ) {
// write code here
if(head==NULL||head->next==NULL)
return head;
struct ListNode *fast=head;
struct ListNode *slow=head;
struct ListNode *brk=NULL;
while(fast&&fast->next)
{
fast=fast->next->next;
if(fast==NULL||fast->next==NULL)
brk=slow;
slow=slow->next;
}
brk->next=NULL;
struct ListNode*head1=sortInList(head);
struct ListNode*head2=sortInList(slow);
struct ListNode dummy;
struct ListNode*cur=&dummy;
while(head1||head2)
{
if(head1==NULL||(head1!=NULL&&head2!=NULL&&head1->val>head2->val))
{
cur->next=head2;
head2=head2->next;
cur=cur->next;
}
else
{
cur->next=head1;
head1=head1->next;
cur=cur->next;
}
}
return dummy.next;
}