单链表相关问题拓展
(注:这段时间学习完数据结构的链表部分的相关知识后,整理了“O(n)单链表中找到数第K个结点下”、“”判断单链表是否有环,并且找到入环的第一个结点的三种方法、 时间复杂度为O(n)”、“判断两个单链表是否相交 时间复杂度为O(n)”、将单链表逆置、"O(1)删除结点"相关代码,用来巩固自己的知识,也希望能够帮助大家在这部分的知识学习)
函数声明文件:
#ifndef __PRACTICE_H
#define __PRACTICE_H
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <malloc.h>
typedef int ElemType;
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef struct Linklist
{
int count;
struct Node head;
}Linklist, *Plist;
//初始化单链表函数
void InitLinkList(Plist list);
//单链表插入结点函数
void InsertLinkList(Plist list, ElemType val, int pos);
//顺序输出单链表函数
void Show(Plist list);
//构建一个带环的单链表,入环结点为第k个结点
void HasRingLinkList(Plist list,int k);
//单链表中找到数第K个结点 时间复杂度为O(n)
ElemType FindNode(Plist list, int k);//返回值为第k个结点的数据
//改正
int FindNode2(Plist list, int k, ElemType* res);//成功返回“1”,失败返回“0”,指针“res”带回倒数第“K”个结点的值
//判断单链表是否有环,并且找到入环的第一个结点 时间复杂度为O(n)
/*
先判断链表是否有环:定义两个指针p和q,p指针每走一步,q指针走两步,如果两个指针相遇,就代表链表有环,如果两个指针任意一个途中为NULL则代表没有环
找入环点的三种思想 :
1:在有环的基础上再定义一个指针m指向p或q的位置,然后p走一步p从自己的位置遍历一次后再到自己的位置,比较p和q,相同后就是入环的结点
2:在有环的基础上将带环的链表从指针pq相遇的位置断开,变成两个链表相交的问题解决
3:在有环的基础利用数学公式解决问题,具体思路在代码中体现
*/
void IsRing(Plist list);//返回值为入环的结点位置
//改正:
Node *IsRing2(Plist list);//利用思路2找结点
Node *IsRing3(Plist list);//利用思路3找结点
//判断两个单链表是否相交 时间复杂度为O(n)
int IsIntresect(Plist firstlist,Plist secondlist);//如果相交返回值为真,如果不相交返回值为假
//改正
int IsIntresect2(Plist firstlist,Plist secondlist);//如果相交返回值为真,如果不相交返回值为假
//构建两个相交的链表,相交的结点个数为k
void IsIntresectLinklist(Plist firstlist,Plist secondlist,int k);
//将单链表逆置
void InversionLinkList(Plist list);
//改正
void InversionLinkList1(Plist list);
//O(1)删除结点p
int DeleteNode(Plist list,Node *p);//p指向链表中某个结点,删除成功时返回“1”,失败返回“0”
//改正
int DeleteNode1(Plist list,Node *p);
#endif
函数实现文件:
#include "Practices.h"
//初始化单链表函数
void InitLinkList(Plist list)
{
list->count = 0;
list->head.next = NULL;
}
//插入结点函数
static Node *BuyNode(ElemType val, Node *next)
{
Node *s = (Node *)malloc(sizeof(Node));
assert(s != NULL);
s->data = val;
s->next = next;
return s;
}
void InsertLinkList(Plist list, ElemType val, int pos)
{
assert(list != NULL);
if( pos < 0 || pos > list->count)
{
printf("pos is error\n");
return ;
}
Node *p = &list->head;
while( pos > 0)
{
p = p->next;
pos --;
}
Node *s = BuyNode(val, p->next);
p->next = s;
list->count ++;
}
//顺序输出单链表函数
void Show(Plist list)
{
assert(list != NULL);
Node *p = list->head.next;
printf("The data of this single linked list is:");
while( p != NULL)
{
printf("%3d",p->data);
p = p->next;
}
printf("\n");
}
//构建一个带环的单链表,入环结点为第k个结点
void HasRingLinkList(Plist list,int k)
{
assert(list != NULL);
if( k < 1 || k > (list->count - 1))
{
printf("pos is error\n");
}
int i = list->count - k;
Node *head = &list->head;
Node *tail = &list->head;
while(i > 0)//让头指针先跑
{
head = head->next;
i--;
}
while( head->next != NULL)
{
head = head->next;
tail = tail->next;
}
printf("last node'data:%d\n",head->data);
head->next = tail;
printf("ring node 'data:%d\n",head->next->data);
}
//单链表中找到数第K个结点 时间复杂度为O(n)(返回值为第k个结点的数据)
ElemType FindNode(Plist list, int k)
{
assert(list != NULL);
Node *first = &list->head;
Node *last = &list->head;
while( k > 0)
{
first = first->next;
k--;
}
while( first->next != NULL)
{
first = first->next;
last = last->next;
}
return last->next->data;
}//没有判断“K”是否为有效值
//改正:
int FindNode2(Plist list, int k, ElemType* res)//用指针“res”把第“K”个结点的值带出来
{
assert(list != NULL);
if(k < 0 || k > list->count) //判断“K”是否为有效值,如果失败返回0
{
return 0;
}
Node *first = list->head.next;
Node *last = first;
while( k > 0)
{
first = first->next;
k--;
}
while(first != NULL)
{
first = first->next;
last = last->next;
}
*res = last->data; //用指针带回数据
return 1; //成功返回1
}
//判断单链表是否有环,并且找到入环的第一个结点 时间复杂度为O(n),(返回值为入环的结点位置)
void IsRing(Plist list)
{
assert( list!= NULL);
int tmpos = list->count;
int i = 0;
Node *p = &list->head;
Node *s = &list->head;
while( tmpos > 0)
{
p = p->next;
tmpos --;
}
if( p->next != NULL)//最后一个结点的next域不为空,则代表此链表有环
{
p = p->next;
while( 1 )
{
if( p->data == s->data )
{
printf("This list has a ring,the node of the entry loop is: %d\n",i);
break;
}
s = s->next;
i++;
}
}
else
{
printf("This list has no rings\n");
}
}//
//改正
Node * IsRing2(Plist list)//利用思路2找结点
{
assert(list != NULL);
//先判断是否有环
struct Node *fast = list->head.next; //快指针,每次走两步
struct Node *slow = fast; //慢指针,每次走一步
while(fast != NULL && fast->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
if( fast == slow)
{
break;
}
}
if(fast == NULL || fast->next == NULL)
{
return NULL; //如果进入这个if判断句,就证明链表没有环,返回一个空指针
}
//下面的代码开始找入环节点
fast = fast->next; //让快指针先走一步,作为拆分后的链表的头
slow->next = NULL;//从两个指针相遇的结点开始断开
slow = list->head.next; //让慢指针作为拆分后另一个链表的头
int f_count = 0; //分别求出两个链表的长度
int s_count = 0;
struct Node *new_fast = fast; // 利用末尾结点后继为空遍历两个链表求出结点数,但要注意的是,
struct Node *new_slow = slow;//因为遍历链表头指针会走,所以得提前记住两个链表的头位置
while( fast != NULL)
{
f_count++;
fast = fast->next;
}
while( slow != NULL)
{
s_count++;
slow = slow->next;
}
int count = 0;
if( f_count > s_count)// 比较两个链表的大小,让较长的一个链表先走结点差值
{
count = f_count - s_count;
while(count > 0)
{
new_fast = new_fast->next;
count --;
}
}
else
{
count = s_count - f_count;
while( count > 0)
{
new_slow = new_slow->next;
count --;
}
}
while( 1 ) //因为前面已经确定这两个链表会相交,所以当两个指针相遇就是入环结点
{
new_fast = new_fast->next;
new_slow = new_slow->next;
if( new_fast == new_slow)
{
return new_fast;
}
}
}
Node *IsRing3(Plist list)//利用思路3找结点
{
assert(list != NULL);
//先判断是否有环
struct Node *fast = list->head.next;
struct Node *slow = fast;
while(fast != NULL && fast->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
if( fast == slow)
{
break;
}
}
if(fast == NULL || fast->next == NULL)
{
return NULL;
}
fast = fast->next;
int circle_count = 1;
while( fast != slow)//求环的大小
{
circle_count++;
fast = fast->next;
}
fast = list->head.next; //把快慢指针都指向链表的头
slow = fast;
while( circle_count > 0)//让快指针先走一个环的大小
{
fast = fast->next;
circle_count--;
}
while(1)//然后两个指针一起往后走
{
if(fast == slow)
{
return fast;
}
fast = fast->next;
slow = slow->next;
}
}
//构建两个相交的链表,相交的结点个数为k
void IsIntresectLinklist(Plist firstlist,Plist secondlist,int k)
{
assert(firstlist != NULL);
assert(secondlist != NULL);
int count = firstlist->count > secondlist->count ? secondlist->count : firstlist->count;
if( k > count)
{
printf("pos is error\n");
}
int firstcoount = firstlist->count - k;
int secondcount = secondlist->count - k;
Node *firsrnode = &firstlist->head;
Node *secondnode = &secondlist->head;
while(firstcoount > 0)
{
firsrnode = firsrnode->next;
firstcoount--;
}
while(secondcount > 0)
{
secondnode = secondnode->next;
secondcount--;
}
secondnode->next = firsrnode->next;
}
//判断两个单链表是否相交 时间复杂度为O(n),(如果相交返回值为真,如果不相交返回值为假)
int IsIntresect(Plist firstlist,Plist secondlist)
{
assert(firstlist != NULL);
assert(secondlist != NULL);
int count = firstlist->count > secondlist->count ? secondlist->count : firstlist->count;
printf("count = %d\n",count);
int firstcount = firstlist->count - count;
int secondcount = secondlist->count - count;
Node *firstnode = &firstlist->head;
Node *secondnode = &secondlist->head;
while( firstcount > 0)
{
firstnode = firstnode->next;
firstcount--;
}
while( secondcount > 0)
{
secondnode = secondnode->next;
secondnode--;
}
while( firstnode->next != NULL)
{
if(firstnode->next == secondnode->next)
{
return 1;
}
firstnode = firstnode->next;
secondnode = secondnode->next;
}
return 0;
}//思路正确但语句过于复杂
//改正
int IsIntresect2(Plist list1,Plist list2)
{
assert( list1 != NULL);
assert( list2 != NULL);
struct Node *p = list1->head.next;
struct Node *q = list2->head.next;
if(list1->count > list2->count)
{
for( int i = 0; i < list1->count - list2->count; i++)
{
p = p->next;
}
}
else
{
for( int i = 0; i < list2->count - list1->count; i++)
{
q = q->next;
}
}
while( p != NULL)
{
if( p == q)
{
return 1;
}
p = p->next;
q = q->next;
}
return 0;
}
//将单链表逆置
void InversionLinkList(Plist list)
{
assert(list != NULL);
Node *first = list->head.next;
Node *middle = first->next;
Node *last = middle->next;
while(last->next != NULL)
{
if(first == list->head.next)
{
first->next = NULL;
}
middle->next = first;
first = middle;
middle = last;
last = last->next;
}
middle->next = first;
last->next = middle;
list->head.next = last;
}//没有考虑只有一个头结点时的情况,语句要简单
//改正:
void InversionLinkList1(Plist list)
{
assert(list != NULL);
struct Node *m = list->head.next;
struct Node *f = NULL;
if( m == NULL) //判断链表是否只有一个头结点,如果只有一个结点就直接返回
{
printf("error\n");
return ;
}
struct Node *l = m->next;
while(1)
{
m->next = f;
f = m;
m = l;
if( m == NULL)
{
break;
}
l = l->next;
}
list->head.next = f;
}
//O(1)删除结点p
int DeleteNode(Plist list,Node *p)
{
assert(list != NULL);
struct Node *q = p->next; //如果q指向的是最后一个结点时,这一步会使下面的语句崩掉
p->data = q->data;
p->next = q->next;
free(q);
list->count--;
return 1;
}//为考虑特殊情况下的结点删除
//改正
int DeleteNode1(Plist list,Node *p)
{
assert(list != NULL);
if(p->next == NULL)//考虑链表中只有一个结点,或者删除的结点是最后一个结点的情况
{
struct Node *q = list->head.next;
while(q->next != p)//注意这里不能用“q->next = NULL”来遍历链表寻找p结点的前驱,因为等q->next == NULL时,q已经指向的时最后一个结点了
{
q = q->next;
}
q->next = NULL;
free(p);
list->count--;
return 1;
}
struct Node *q = p->next;
p->data = q->data;
p->next = q->next;
free(q);
list->count--;
return 1;
}
代码测试:
#include "Practices.h"
int main()
{
//测试求带环结点的入环结点3
Linklist list;
InitLinkList(&list);
for( int i = 0; i < 10; i++)
{
InsertLinkList(&list, i * 10, i);
}
Show(&list);
HasRingLinkList(&list,1);
Node *p = IsRing3(&list);;
printf("入环结点的数据为%d\n",p->data);
//测试求带环链表的入环结点2
/* Linklist list;
InitLinkList(&list);
for( int i = 0; i < 10; i++)
{
InsertLinkList(&list, i * 10, i);
}
Show(&list);
HasRingLinkList(&list,5);
Node *p = IsRing2(&list);
printf("入环结点的数据为%d\n",p->data);
*/
//测试逆置单链表
/* Linklist list;
InitLinkList(&list);
for( int i = 0; i < 4; i++)
{
InsertLinkList(&list, i * 10, i);
}
Show(&list);
InversionLinkList1(&list);
Show(&list);
*/
//测试O(1)删除结点p
/* Linklist list1;
Plist list2 = &list1;
InitLinkList(&list1);
for( int i = 0; i < 6; i++)
{
InsertLinkList(&list1, i * 10, i);
}
Show(&list1);
Node *p = list2->head.next;
DeleteNode(list2, p);
Show(&list1);
*/
//测试两个链表是否相交
/* Linklist list1;
Linklist list2;
InitLinkList(&list1);
InitLinkList(&list2);
for( int i = 0; i < 6; i++)
{
InsertLinkList(&list1, i * 10, i);
InsertLinkList(&list2, i, i);
}
Show(&list1);
Show(&list2);
IsIntresectLinklist(&list1, &list2, 2);
Show(&list1);
Show(&list2);
if(IsIntresect2(&list1,&list2))
{
printf("Is true\n");
}
else
{
printf("Is false\n");
}
*/
//测试单链表中找到数第K个结点
/* ElemType res;
if(FindNode2(&list, 3, &res))
{
printf("Looking for successful\n");
}
for(int i = 1; i < 6; i++)
{
if(FindNode2(&list, i, &res))
{
printf("倒数第%d个结点的数值为%d\n",i,res);
}
}
*/
return 0;
}
附1: 寻找带环结点链表思想二图示
附2: 寻找带环结点链表思想三图示