C语言单向链表环测试并返回环起始节点
- 格式:doc
- 大小:34.00 KB
- 文档页数:5
数据结构遍历单循环链表,输出所有节点信息要遍历单循环链表并输出所有节点的信息,可以使用递归来实现。
具体步骤如下:1. 定义一个函数`Node`来代表链表中的节点,并定义一个指向链表头节点的指针`head`作为函数参数。
2. 将链表首节点的指针`head`作为函数参数传递给函数`Node`,并将`head`指向链表的第一个节点。
3. 在函数`Node`中,定义一个内部变量`next`用于保存指向下一个节点的指针,并将其初始化为链表头节点的指针。
4. 遍历链表并输出节点信息。
在遍历过程中,使用`next`指针来访问每个节点,并使用函数`Node`中的指针来存储节点的信息。
下面是代码示例:```c++#include <iostream>#include <cstring>using namespace std;class Node {public:Node(int data) {data_ = data;next_ = NULL;}int data_;Node* next_;// 构造函数,用于初始化节点数据Node(int val) : data_(val), next_(NULL) {} };int main() {Node* head = new Node(1);head->next = new Node(2);head->next->next = new Node(3);head->next->next->next = new Node(4);cout << "The head node is: ";Node* p = head;while (p != NULL) {cout << p->data_ << " ";p = p->next;}cout << endl;return 0;}```输出结果为:```The head node is: 1 2 3 4```在这个示例中,我们定义了一个`Node`类来表示链表节点,并定义了一个指向链表头节点的指针`head`作为函数参数。
c语言单链表的基本操作C语言的单链表是一种常见的数据结构,常常用于存放数据的操作。
在实际开发中,掌握C语言单链表的基本操作是非常重要的。
下面,我们将分步骤阐述C语言单链表的基本操作。
第一步:定义单链表节点的结构体单链表的每个节点都有三个部分组成:数据域、指针域和链头。
其结构体如下所示:```struct Listnode{int data; //数据域struct Listnode* next; //指针域};```第二步:创建单链表创建单链表的方法有很多,这里我们介绍一个使用头插法的创建方法。
该方法需要定义一个头节点,然后将新的节点插到头节点后面。
代码如下所示:```struct Listnode * create(){struct Listnode * head = NULL; //定义头节点为空int x; //定义数据变量xprintf("请输入数据:");while (scanf("%d", &x) != EOF) //判断当前输入是否结束{struct Listnode * p = (structListnode*)malloc(sizeof(struct Listnode)); //利用malloc函数为新节点分配内存p->data = x; //将x的值存储进新开辟的节点p->next = head; //将head指向的节点作为p节点的下一个节点head = p; //然后将p作为新的head}return head; //返回头节点}```第三步:遍历单链表遍历单链表需要用到while循环,直到链表中没有节点可以遍历。
遍历的过程中,可以利用指针打印节点的数据值。
代码如下所示:```void traverse(struct Listnode *head){if (head == NULL) //判断链表是否为空printf("链表为空!");struct Listnode * p = head; //定义一个指向head节点的指针 while (p != NULL) //当指针p不为空时{printf("%d ", p->data); //打印节点中的数据p = p->next; //指针p指向下一个节点}}```第四步:插入节点在插入节点前,需要先找到插入位置的前一个节点。
c语⾔实现--单向循环链表操作1,什么叫单向循环链表。
单向循环链表是指在单链表的基础上,表的最后⼀个元素指向链表头结点,不再是为空。
2,由图可知,单向循环链表的判断条件不再是表为空了,⽽变成了是否到表头。
3,链表的结点表⽰1struct LNode2 {3int data;4struct LNode * next;5 };6 typedef struct LNode * linklist4,单向循环链表的操作集合,仍是defs.h⾥的操作集合,这⾥就不给出了。
5,单循环链表的初始化操作。
⽰意图实现:1 #include"defs.h"23void InitList(linklist *L) //改变尾指针4 {5 *L = (linklist)malloc(sizeof(struct LNode)); //分配头结点6if (*L == NULL) //分配失败7 exit(0);8 (*L)->next = *L; //指针域指向它本⾝9 }6,清空操作最終图和初始化的结果是⼀样的。
1 #include"defs.h"23void ClearList(linklist *L) //改变尾指针4 {5 linklist p, q;6 *L = (*L)->next; //先令尾指针指向头结点,不然释放最后⼀个结点时尾指针,⽆法指向头结点7 p = (*L)->next; //p指向第⼀个结点89while (p != *L) //p未到表头时10 {11 q = p->next;12 free(p);13 p = q;14 }15 *L->next = *L; //头结点指针域指向其本⾝16 }7,DestroyList操作。
撤销操作是在清空操作基础上,释放了头结点。
1 #include"defs.h"23void DestroyList(linklist *L)4 {5 ClearList(&L);6 free(*L); //释放头结点7 *L = NULL;8 }DestroyList.c8,ListEmpty操作。
c语言数据结构单循环链表单向循环链表是一种特殊的链表,它的尾节点指向头节点,形成一个环状结构。
在C语言中,我们可以通过定义一个结构体来表示单向循环链表的节点,结构体中包含一个指针域,指向下一个节点。
下面是一个简单的C语言实现单向循环链表的例子:c#include#include// 定义链表节点结构体typedef struct Node {int data; // 节点数据struct Node* next; // 指向下一个节点的指针} Node;// 创建链表节点Node* createNode(int data) {Node* newNode = (Node*)malloc(sizeof(Node));if (newNode == NULL) {printf("内存分配失败\n");exit(1);}newNode->data = data;newNode->next = NULL;return newNode;}// 在链表末尾插入节点void insertAtEnd(Nodehead, int data) {Node* newNode = createNode(data);if (*head == NULL) {*head = newNode;(*head)->next = *head;} else {Node* temp = *head;while (temp->next != *head) {temp = temp->next;}temp->next = newNode;newNode->next = *head;}}// 在链表开头插入节点void insertAtBeginning(Nodehead, int data) { Node* newNode = createNode(data);if (*head == NULL) {*head = newNode;(*head)->next = *head;} else {newNode->next = *head;Node* temp = *head;while (temp->next != *head) {temp = temp->next;}temp->next = newNode;*head = newNode;}}// 删除指定位置的节点void deleteNode(Nodehead, int position) { if (*head == NULL) {printf("链表为空\n");return;}Node* temp = *head;if (position == 0) {while (temp->next != *head) {temp = temp->next;}Node* delNode = *head;if (delNode->next == *head) {*head = NULL;} else {*head = (*head)->next;temp->next = *head;}free(delNode);} else {Node* prev = NULL;int count = 0;while (temp->next != *head && count < position) { prev = temp;temp = temp->next;count++;}if (count != position) {printf("位置超出链表长度\n");return;}prev->next = temp->next;free(temp);}}// 打印链表void printList(Node* head) {if (head == NULL) {printf("链表为空\n");return;}Node* temp = head;do {printf("%d ", temp->data);temp = temp->next;} while (temp != head);}int main{Node* head = NULL;// 在链表末尾插入节点insertAtEnd(&head, 1);insertAtEnd(&head, 2);insertAtEnd(&head, 3);// 在链表开头插入节点insertAtBeginning(&head, 0);// 删除指定位置的节点deleteNode(&head, 2);// 打印链表printList(head);return 0;}在上面的代码中,我们定义了一个`Node`结构体来表示链表节点,其中`data`字段用于存储节点的数据`next`字段用于指向下一个节点。
链表c语言经典例题
链表是计算机科学中的经典数据结构之一,常用于存储和操作动态数据。
以下是一些常见的链表例题,可以帮助理解链表的基本操作和应用。
1. 链表的创建:
- 创建一个空链表。
- 创建一个包含指定节点值的链表。
2. 链表的插入操作:
- 在链表的头部插入一个节点。
- 在链表的尾部插入一个节点。
- 在指定位置插入一个节点。
3. 链表的删除操作:
- 删除链表的头节点。
- 删除链表的尾节点。
- 删除指定数值的节点。
4. 链表的查找操作:
- 查找链表中指定数值的节点。
- 查找链表的中间节点。
5. 链表的逆序操作:
- 反转整个链表。
- 反转链表的前 N 个节点。
- 反转链表的一部分区间内的节点。
6. 链表的合并操作:
- 合并两个有序链表,使其有序。
- 合并 K 个有序链表,使其有序。
7. 链表的环检测:
- 判断链表中是否存在环,若存在,则返回环的起始节点。
8. 链表的拆分操作:
- 将一个链表按照奇偶位置拆分成两个链表。
以上是一些链表的经典例题,通过解答这些例题,可以加深对链表结构和基本操作的理解。
在编写对应的 C 语言代码时,需要注意链表节点的定义、指针的使用以及内存的动态分配和释放等问题。
C语⾔实现单向链表及其各种排序(含快排,选择,插⼊,冒泡) #include<stdio.h>#include<malloc.h>#define LEN sizeof(struct Student)struct Student //结构体声明{long num;int score;struct Student* next;};int n;struct Student* creat() //创建单向链表{struct Student *head=NULL, *p_before, *p_later;p_before = p_later = (struct Student*) malloc(LEN);scanf_s("%ld%d", &p_before->num, &p_before->score);while (p_before->num!=0){n++;if (n == 1) head = p_before;else p_later->next = p_before;p_later = p_before;p_before = (struct Student*) malloc(LEN);scanf_s("%ld%d", &p_before->num, &p_before->score);}p_later->next = NULL;free(p_before);return head;}struct Student* sort(struct Student* list) //冒泡排序,当初写的是内容交换⽽不是指针交换,我知道这不是好的做法,但⽇⼦⼀久,当下没时间和热情改了,⼤家原谅,{ //等有时间了⼀定改struct Student *p, *q;int temp1,i;long temp2;for (p = list, i =1; i < n;i++,p=p->next)for (q = p->next;q!= NULL;q=q->next)if (p->score < q->score){temp1 = p->score;p->score = q->score;q->score = temp1;temp2 = p->num;p->num = q->num;q->num = temp2;}return list;}struct Student* sort1(struct Student* h) //插⼊排序(下边这堆注释是当初写完代码后⼜分析时加的,这⾥必须承认,我参考了⽹上的⼀些代码。
CC++实现单向循环链表(尾指针,带头尾节点) C语⾔实现单向循环链表,主要功能为空链表创建,链表初始化(头插法,尾插法),链表元素读取,按位置插⼊,(有序链表)按值插⼊,按位置删除,按值删除,清空链表,销毁链表。
单向循环链表和单向链表的区别:(1)单向链表为头指针,循环链表为尾指针,头指针指向头结点,尾指针指向终端结点;(2)为统⼀⽅便操作,单向链表设置头结点,单向循环链表设置头结点和尾结点;(3)设置尾结点后,尾指针指向尾结点,插⼊,删除等操作不⽤移动尾指针。
关键思路:创建头结点和尾结点。
1 #include <stdio.h>2 #include <stdlib.h>34 typedef struct Node{5int data;6struct Node *next;7 }Node;89//空循环链表创建10//创建头结点和尾结点11//链表尾指针指向尾结点,尾结点指向头结点,头结点指向尾结点12void iniCList(Node **CListTail){13 *CListTail = (Node *)malloc(sizeof(Node));14 Node *CListHead = (Node *)malloc(sizeof(Node));15if (NULL == *CListTail || NULL == CListHead){16 exit(0);17 }1819 (*CListTail)->next = CListHead;20 CListHead->next = *CListTail;21 }2223//循环链表初始化(头插法)24void iniCListHead(Node **CListTail, int n){25//创建头尾结点26 *CListTail = (Node *)malloc(sizeof(Node));27 Node *CListHead = (Node *)malloc(sizeof(Node));28if (NULL == *CListTail || NULL == CListHead){29 exit(0);30 }3132 (*CListTail)->next = CListHead;33 CListHead->next = *CListTail;3435int i = 0;36while (i < n){3738 Node *tmpNode = (Node *)malloc(sizeof(Node));39if (NULL == tmpNode){40 exit(0);41 }42 tmpNode->data = i;43 tmpNode->next = CListHead->next;44 CListHead->next = tmpNode;45 ++i;46 }47 }4849//循环链表初始化(尾插法)50void iniCListTail(Node **CListTail, int n){51//创建头尾结点52 *CListTail = (Node *)malloc(sizeof(Node));53 Node *CListHead = (Node *)malloc(sizeof(Node));54if (NULL == *CListTail || NULL == CListHead){55 exit(0);56 }5758 (*CListTail)->next = CListHead;59 CListHead->next = *CListTail;6061 Node *pCurrent = CListHead;6263int i = 0;64while (i < n){65 Node *tmpNode = (Node *)malloc(sizeof(Node));66if (NULL == tmpNode){67 exit(0);68 }69 tmpNode->data = i;70 tmpNode->next = *CListTail;71 pCurrent->next = tmpNode;72 pCurrent = tmpNode;7374 ++i;75 }76 }7778//循环链表按位置插⼊79void insertCListPos(Node *CList, int pos, int val){ 8081 Node *pCurrent = CList->next; //指向头结点82int i = 1;83while (pCurrent != CList && i < pos){84 pCurrent = pCurrent->next;85 ++i;86 }8788 Node *tmpNode = (Node *)malloc(sizeof(Node)); 89if (NULL == tmpNode){90 exit(0);91 }92 tmpNode->data = val;93 tmpNode->next = pCurrent->next;94 pCurrent->next = tmpNode;9596 }9798//有序循环链表,按值插⼊99void insertCListValue(Node *CList, int val){100 Node *pCur = CList->next->next;101 Node *pPer = CList->next;102103while (pCur != CList && pCur->data < val){ 104 pPer = pCur;105 pCur = pCur->next;106 }107108 Node *tmpNode = (Node *)malloc(sizeof(Node)); 109if (NULL == tmpNode){110 exit(0);111 }112 tmpNode->data = val;113 tmpNode->next = pPer->next;114 pPer->next = tmpNode;115 }116117//循环链表,按位置删除118void deleteCListPos(Node *CList, int pos){119 Node *pCur = CList->next;120121int i = 1;122while (pCur != CList && i < pos){123 pCur = pCur->next;124 ++i;125 }126127 Node *tmpNode = pCur->next;128 pCur->next = tmpNode->next;129free(tmpNode);130 }131132//循环链表,按值删除133//删除空链表为出问题134void deleteCListValue(Node *CList, int val){135 Node *pCur = CList->next->next;136 Node *pPer = CList->next;137138while (pCur != CList && pCur->data != val){ 139 pPer = pCur;140 pCur = pCur->next;141 }142if (pCur == CList)143return;144else{145 pPer->next = pCur->next;146free(pCur);147 }148 }149150//循环链表,清空链表151void claerCList(Node *CList){152 Node *p = CList->next->next;153 Node *q = NULL;154155while (p != CList){ //到达表尾156 q = p->next;157free(p);158 p = q;159 }160161 CList->next = CList; //将头结点指向尾结点162 }163164//循环链表,销毁链表165void destoryCList(Node **CList){166 Node *p = (*CList)->next;167 Node *q = NULL;168169while (p != (*CList)->next){ //到达表头170 q = p->next;171free(p);172 p = q;173 }174175 *CList = NULL;176 }177178//获取元素179void getCList(Node *CList, int pos, int *val){180 Node *pCur = CList->next->next;181int i = 1;182while (pCur != CList && i < pos){183 pCur = pCur->next;184 ++i;185 }186187 *val = pCur->data;188 }189//遍历输出元素190void printCList(Node *CList){191 Node * tmpNode = CList->next->next;192while (tmpNode != CList){ //到达表尾193 printf("%d\n", tmpNode->data);194 tmpNode = tmpNode->next;195 }196 }197198199int main(){200 Node *CList = NULL;201//iniCListHead(&CList, 8);202//iniCList(&CList);203 iniCListTail(&CList, 8);204205//insertCListPos(CList, 1, 2);206//insertCListPos(CList, 2, 4);207//insertCListPos(CList, 3, 6);208//209//insertCListValue(CList, 1);210//211//deleteCListPos(CList, 3);212//213//deleteCListValue(CList, 6);214215//claerCList(CList);216217int a = 0;218 getCList(CList, 2, &a);219 printf("%d\n", a);220221 printCList(CList);222223 printf("%d\n", CList);224 destoryCList(&CList);225 printf("%d\n", CList);226227 system("pause");228return0;229 }C语⾔完整代码 通过C++实现C语⾔的链表,主要区别:(1)struct可以不通过typedef,直接使⽤Node;(2)将malloc和free更换为new和delete1 #include <stdio.h>2 #include <stdlib.h>34struct Node{5int data;6struct Node *next;7 };89//空循环链表创建10//创建头结点和尾结点11//链表尾指针指向尾结点,尾结点指向头结点,头结点指向尾结点 12void iniCList(Node **CListTail){13 *CListTail = new Node;14 Node *CListHead = new Node;1516 (*CListTail)->next = CListHead;17 CListHead->next = *CListTail;18 }1920//循环链表初始化(头插法)21void iniCListHead(Node **CListTail, int n){22//创建头尾结点23 *CListTail = new Node;24 Node *CListHead = new Node;2526 (*CListTail)->next = CListHead;27 CListHead->next = *CListTail;2829int i = 0;30while (i < n){31 Node *tmpNode = new Node;3233 tmpNode->data = i;34 tmpNode->next = CListHead->next;35 CListHead->next = tmpNode;36 ++i;37 }38 }3940//循环链表初始化(尾插法)41void iniCListTail(Node **CListTail, int n){42//创建头尾结点43 *CListTail = new Node;44 Node *CListHead = new Node;4546 (*CListTail)->next = CListHead;47 CListHead->next = *CListTail;4849 Node *pCurrent = CListHead;5051int i = 0;52while (i < n){53 Node *tmpNode = new Node;5455 tmpNode->data = i;56 tmpNode->next = *CListTail;57 pCurrent->next = tmpNode;58 pCurrent = tmpNode;5960 ++i;61 }62 }6364//循环链表按位置插⼊65void insertCListPos(Node *CList, int pos, int val){6667 Node *pCurrent = CList->next; //指向头结点68int i = 1;69while (pCurrent != CList && i < pos){70 pCurrent = pCurrent->next;71 ++i;72 }7374 Node *tmpNode = new Node;7576 tmpNode->data = val;77 tmpNode->next = pCurrent->next;78 pCurrent->next = tmpNode;7980 }8182//有序循环链表,按值插⼊83void insertCListValue(Node *CList, int val){84 Node *pCur = CList->next->next;85 Node *pPer = CList->next;8687while (pCur != CList && pCur->data < val){88 pPer = pCur;89 pCur = pCur->next;90 }9192 Node *tmpNode = new Node;9394 tmpNode->data = val;95 tmpNode->next = pPer->next;96 pPer->next = tmpNode;97 }9899//循环链表,按位置删除100void deleteCListPos(Node *CList, int pos){ 101 Node *pCur = CList->next;102103int i = 1;104while (pCur != CList && i < pos){105 pCur = pCur->next;106 ++i;107 }108109 Node *tmpNode = pCur->next;110 pCur->next = tmpNode->next;111delete tmpNode;112 }113114//循环链表,按值删除115//删除空链表为出问题116void deleteCListValue(Node *CList, int val){ 117 Node *pCur = CList->next->next;118 Node *pPer = CList->next;119120while (pCur != CList && pCur->data != val){ 121 pPer = pCur;122 pCur = pCur->next;123 }124if (pCur == CList)125return;126else{127 pPer->next = pCur->next;128delete pCur;129 }130 }131132//循环链表,清空链表133void claerCList(Node *CList){134 Node *p = CList->next->next;135 Node *q = NULL;136137while (p != CList){ //到达表尾138 q = p->next;139delete p;140 p = q;141 }142143 CList->next = CList; //将头结点指向尾结点144 }145146//循环链表,销毁链表147void destoryCList(Node **CList){148 Node *p = (*CList)->next;149 Node *q = NULL;150151while (p != (*CList)->next){ //到达表头152 q = p->next;153delete p;154 p = q;155 }156157 *CList = NULL;158 }159160//获取元素161void getCList(Node *CList, int pos, int *val){ 162 Node *pCur = CList->next->next;163int i = 1;164while (pCur != CList && i < pos){165 pCur = pCur->next;166 ++i;167 }168169 *val = pCur->data;170 }171//遍历输出元素172void printCList(Node *CList){173 Node * tmpNode = CList->next->next;174while (tmpNode != CList){ //到达表尾175 printf("%d\n", tmpNode->data);176 tmpNode = tmpNode->next;177 }178 }179180181int main(){182 Node *CList = NULL;183//iniCListHead(&CList, 8);184//iniCList(&CList);185 iniCListTail(&CList, 8);186187//insertCListPos(CList, 1, 2);188//insertCListPos(CList, 2, 4);189//insertCListPos(CList, 3, 6);190//191//insertCListValue(CList, 1);192//193//deleteCListPos(CList, 3);194//195//deleteCListValue(CList, 6);196197//claerCList(CList);198199int a = 0;200 getCList(CList, 2, &a);201 printf("%d\n", a);202203 printCList(CList);204205 printf("%d\n", CList);206 destoryCList(&CList);207 printf("%d\n", CList);208209 system("pause");210return0;211 }C++完整代码单向循环链表 注意:(1)单向循环链表销毁时,需要将头结点和尾结点删除;(2)单向循环链表插⼊,删除,遍历,清空链表时,条件从头结点或第⼀节点始,判断指针是否达到尾结点;(3)清空链表时,最后将头结点指向尾结点;(4)销毁链表时,条件从头结点始,判断条件为指针是否到达头结点,最后将指针置空。
用c语言实现的,判断链表有没有环,环的入口,两个链表有没有交叉点地磁学A题目:①判断一个单向链表是否有环,如果有环则找到环的入口节点。
②判断两个单向链表是否相交,如果相交则找到交点节点。
发i算法思想:①用两个指针p1,p2同时指向链表的头部,p1一次移动一步,p2一次移动两步,如果最终p1和p2重合则说明链表有环,如果p2走到空指针(链表的结尾)则说明链表无环。
如果最终p1和p2重合,使p2重新指向链表的头结点,然后p1和p2同时一次移动一步,当p1和p2再次重合时该节点指针就是环的入口节点指针。
②有了第一问的算法基础,应该不难理解第二问。
首先将其中一个链表list1首尾相接,变成一个有环链表,如果另一个链表list2和list1相交的话,list2也将成为一个有环链表,并且环的入口节点就是两个链表的交叉节点。
如果两个链表不相交,则list2依然是一个无环链表。
#include <stdio.h>#include <string.h>#include <malloc.h>typedef struct node{int val;struct node* next;}snode;typedef struct list{snode *head; //队列头指针snode *tail; //队列尾指针}list; //队列单元list * creat_list(int *array,int len){list *mylist =(list*)malloc(sizeof(list));mylist->head = (snode*)malloc(sizeof(snode));mylist->head->val = array[0];mylist->head->next = NULL;snode *p;p = mylist->head;for(int i = 1; i < len; i++){snode *temp;temp = (snode *)malloc(sizeof(snode));temp->val = array[i];temp->next = NULL;p->next = temp;p = temp;}mylist->tail = p;return mylist;}snode *zhidingnode(int len,list *mylist){snode *p = mylist->head;才while(--len)p = p->next;return p;}//有没有环snode * find(snode *head) //0 :have 1:none {snode *slow = head;snode *fast = head;while(fast&& fast->next){slow = slow->next;fast = fast->next->next;if(slow == fast){printf("There is a circle!\n");break;}}if(fast == NULL || fast->next == NULL)return NULL;elsereturn slow;}//环的入口snode * find_rukou(snode *head){snode *h = head;snode * s = find(head);while(h != s){h = h->next;s = s->next;}printf("find and the date:%d\n",h->val); return h;}//判断两个链表是否有交叉void FindCross(list list1,list list2){list2.tail->next = list2.head;find_rukou(list1.head);}int main(){int array[8]={1,4,6,4,5,6,7};list * mylist;mylist =(list*)malloc(sizeof(list));mylist = creat_list(array,8);snode* mynode;mynode = zhidingnode(3,mylist);mylist->tail->next = mynode;find_rukou(mylist->head);int array1[]={1,2,3,4,5,6,7};int array2[]={8,9,10,11,12,13,14};list * mylist1,*mylist2;mylist1 = creat_list(array1,7);mylist2 = creat_list(array2,7);snode *zdnode;zdnode = zhidingnode(4,mylist2);mylist1->tail->next = zdnode; FindCross(*mylist1, *mylist2);return 0;}。
c 语言单链表的遍历C C 语语言言中中,,单单链链表表是是一一种种常常见见的的数数据据结结构构,,用用于于存存储储和和组组织织数数据据。
遍遍历历单单链链表表是是指指按按照照一一定定的的顺顺序序访访问问链链表表中中的的每每个个节节点点。
以以下下是是一一个个示示例例程程序序来来遍遍历历单单链链表表的的所所有有节节点点:: ``````c c##i i n n c c l l u u d d e e <<s s t t d d i i o o ..h h >>##i i n n c c l l u u d d e e <<s s t t d d l l i i b b ..h h >>//// 定定义义链链表表节节点点的的结结构构s s t t r r u u c c t t N N o o d d e e {{i i n n t t d d a a t t a a ;; //// 数数据据域域s s t t r r u u c c t t N N o o d d e e ** n n e e x x t t ;; //// 指指向向下下一一个个节节点点的的指指针针 }};;//// 遍遍历历链链表表并并打打印印每每个个节节点点的的数数据据v v o o i i d d t t r r a a v v e e r r s s e e L L i i n n k k e e d d L L i i s s t t ((s s t t r r u u c c t t N N o o d d e e ** h h e e a a d d )) {{ s s t t r r u u c c t t N N o o d d e e ** t t e e m m p p == h h e e a a d d ;; //// 从从头头指指针针开开始始遍遍历历链链表表//// 遍遍历历链链表表直直到到尾尾部部节节点点w w h h i i l l e e ((t t e e m m p p !!== N N U U L L L L )) {{p p r r i i n n t t f f ((""%%d d "",, t t e e m m p p -->>d d a a t t a a ));; //// 打打印印当当前前节节点点的的数数据据t t e e m m p p == t t e e m m p p -->>n n e e x x t t ;; //// 移移动动到到下下一一个个节节点点}}p p r r i i n n t t f f ((""\\n n ""));;}}i i n n t t m m a a i i n n (()) {{//// 创创建建链链表表节节点点s s t t r r u u c c t t N N o o d d e e ** h h e e a a d d == ((s s t t r r u u c c t tN N o o d d e e **))m m a a l l l l o o c c ((s s i i z z e e o o f f ((s s t t r r u u c c t t N N o o d d e e ))));;s s t t r r u u c c t t N N o o d d e e ** s s e e c c o o n n d d == ((s s t t r r u u c c t tN N o o d d e e **))m m a a l l l l o o c c ((s s i i z z e e o o f f ((s s t t r r u u c c t t N N o o d d e e ))));;s s t t r r u u c c t t N N o o d d e e ** t t h h i i r r d d == ((s s t t r r u u c c t tN N o o d d e e **))m m a a l l l l o o c c ((s s i i z z e e o o f f ((s s t t r r u u c c t t N N o o d d e e ))));;//// 赋赋值值给给链链表表节节点点的的数数据据域域h h e e a a d d -->>d d a a t t a a == 11;;s s e e c c o o n n d d -->>d d a a t t a a == 22;;t t h h i i r r d d -->>d d a a t t a a == 33;;//// 将将链链表表节节点点连连接接起起来来h h e e a a d d -->>n n e e x x t t == s s e e c c o o n n d d ;;s s e e c c o o n n d d -->>n n e e x x t t == t t h h i i r r d d ;;t t h h i i r r d d -->>n n e e x x t t == N N U U L L L L ;; //// 最最后后一一个个节节点点的的指指针针置置为为空空//// 遍遍历历链链表表t t r r a a v v e e r r s s e e L L i i n n k k e e d d L L i i s s t t ((h h e e a a d d ));;//// 释释放放链链表表节节点点的的内内存存f f r r e e e e ((h h e e a a d d ));;f f r r e e e e ((s s e e c c o o n n d d ));;f f r r e e e e ((t t h h i i r r d d ));;r r e e t t u u r r n n 00;;}}``````在在上上面面的的示示例例代代码码中中,,首首先先定定义义了了一一个个链链表表节节点点的的结结构构,,包包括括一一个个整整型型的的数数据据域域和和一一个个指指向向下下一一个个节节点点的的指指针针。
链表是学习数据结构的大门,在微软等大公司招聘c\c++技术人员的时候有3个会必然出现的东西,指针、链表、二叉树!在看一下内容时请确认您已经熟悉C语言的指针,否则请先复习指针的基础知识。
以下以单链表为说明对象!我从一个初学者的角度来对链表进行说明:理论上的东西大家可以参考c语言数据结构一书,在此主要用一些例子来说明:看下面链表的基本形式:Struct link_c{Char name[20];Struct link_c *link;}可以看到这是一个结构体,结构体中有一个link_c的指针link。
如图所示,head是链表的头指针,他指向链表的第一个节点,如果head为空则说明链表是空的。
而节点中的link指向下一节点的首地址,如果link为空则代表此节点为这个链表的最后一个节点(非循环表,循环表的最后一个link指向head)。
这里要注意的是Link要与他后面所指的节点的结构相同,所以其类型必须是link。
下面我们创建一个简单的链表并且进行基本操作。
//定义链表的基础结构体Struct student{Int num;Struct student *next;};//创建一个只有头节点的空链表Struct student *creat()//返回的是一个结构体指针{Struct student *head;//定义并初始化头指针Head = (struct student *)malloc(sizeof(struct student));//如果内存分配失败则返回空If(!head)//这种写法我也很不适应,但是比较规范{Return null;}//head头结点的指向的next为空,链表只有一个节点。
Head->next = NULL;Return head;}以上建立了一个单节点的链表。
接下来可以根据以上代码进行优化修改,创建一个多链接的链表创建有n个节点的链表Struct student *creat( int n )//返回的是一个结构体{Int I;Struct student *head, *p, *s;//定义并初始化头指针Head = (struct student *)malloc(sizeof(struct student));//如果内存分配失败则返回空If(!headl){Return null;}P=head;For(i=0,i<n,i++){S=(struct student*)malloc(sizeof(struct student));If(!s){Return null;}p->next= s;s->next=NULL;p=s;//i+1后s的前驱节点就变成了上一循环的s也就是现在的p ,,,此处有点绕}Return head;返回头节点指针}-----------------------------------------------------------------------------------------上面已经建立了一个比较简单的链表结构,下面进行一些链表的基本操作链表的查询操作Struct student *search(struct student *head, int *num)//head是头指针,num是要查找的数据{struct student *p;//定义一个节点指针int *m;//临时保存节点内的数据p=head->next;//头指针是不包含数据的,所以搜索的第一个节点是头指针指向的节点while(p!=NULL)//没有到最后一个节点{M=P->num;If(strcmp(num,m)==0)//strcmp应用可查csdn{Return p;}Else p=p->next;//如果判断不正确则指向下一个节点}If(p==NULL){//没有找到数据,可以做消息弹出}}接下来是链表的插入操作,将s节点插入到p节点以前设结构体中的int num是一个关键字,链表以这个关键字进行的排序Struct student *instert(struct student *s, struct student *head)//s就是要插入的结构体*插入的节////点必须和原链表结构相同。
C语言单向链表环测试并返回环起始节点
有时候我们需要测试一个单向链表是否存在环。
最土鳖的方法就是改变链表的数据结构,给每个节点添加一个bool变量,在未测试时全部初始化为false,然后遍历链表,每访问一个节点,先测试其bool成员来确定这个节点是否被访问过,如果为true,则表示访问过,则有环,否则设置bool成员为true,表明访问过,然后继续测试。
如果不改变数据结构的话,我们有以下的解决方案:
1. 测试是否有环:
我们可以构建两个迭代器来遍历链表,一个每一次移动一个节点,另外一个每次移动两个节点。
如果这两个一快一慢的土鳖迭代器相遇了,也就是说他们在某个时刻都到了同一个节点,那么我们可以肯定有环存在。
直观的理解就是让两个土鳖一快一慢在400米环形跑道上各选一个位置,然后同时顺时针做死了跑,那么这两个土鳖总能相遇,因为一个比另外一个快。
如果需要严谨的证明,我们可以这样理解。
假设在某个迭代时刻,两个土鳖迭代器(以后简称土鳖)都进入了环,一个距环起始点为i,一个距环起始点为j。
这个假设必然有成立的时候,因为跑着跑着他们总会进入环,而且一旦进入那就出不来了,只能做死了跑。
然后假设又跑了一会儿,这两个土鳖相遇了,一个土鳖跑了x步,一个跑了2x步。
如果这个环总共长n,也就是说慢土鳖需要跑n步才能跑完一圈。
然后我们可以得出i+x和j+2x对于n同余,也就是说i+x和j+2x除以n的余数是相同的,写成同余等式就是(i+x)=j+2x(mod n) ,根据同余加减法性质,我们可以让上面的式子减去x=x(mod m),得到i=(j+x)(mod m)。
因为x未知,所以上面的式子是个同余方程,i、j都是普通整数,很明显这个方程是有解的。
例如2=(1+x)(mod 5)的一个简单解就是1。
所以这两个土鳖跑着跑着总会相遇。
也就是说我们上面检测环的算法可行,不会死循环。
2. 获取环起始点:
基于问题1的分析,快土鳖和慢土鳖总会在某个节点相遇,假设这个点为cross。
同事假设环起始点为start。
一个显然的事实是,当两个土鳖相遇时,慢土鳖跑过的路径是快土鳖的一半。
这样的话,在相遇前,当慢土鳖跑了一般的时候,快土鳖已经经过了相遇点(落脚或者跨越)。
这样的话当慢土鳖跑完后半段的时候,快土鳖从相遇点开始又跑了同样的路程到达了相遇点,这个路程的长度等于慢土鳖总共跑的长度。
现在牛逼的地方来了,如果慢土鳖从头开始跑的时候,有另外一个慢土鳖从相遇点cross开始跑,那么他们两个也会在相遇点相遇,我们称这两个土鳖分别为A和B。
土鳖B走的路程和快土鳖后半段时间走过的路
程是完全一样的,唯一的区别就是他慢一点而已。
现在第二个牛逼的地方来了,因为慢土鳖A和B的速度是一样的,那么他们在相遇点之前的节奏也是一样的,也就是说他们在相遇点值钱已经相遇了,而且一同样的速度相伴走到了相遇点cross。
他们从什么时候相遇开始这段快乐的旅程呢,当然是环起始点start。
我们可以让慢土鳖A和B从相遇点倒退,这样就能理解为什么他们在start点相遇了。
OK,现在我们有了解决方案,让慢土鳖A从链表头start开始跑,让另外一个慢土鳖从相遇点cross开始跑,他们第一次的相遇点就是环起始点。
大功告成,标点符号(废话)有点多,大家不要介意。
下面是C++代码:
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 template<typename T>
5 struct Node
6 {
7 T value;
8 Node* next;
9 };
10
11 //Test if a linked list has circle
12 template<typename T>
13 bool hasLoop(Node<T>* linkedList, Node<T>** loopCross = NULL)
14 {
15 //empty linked list, no circle
16 if(linkedList == NULL || loopCross == NULL) return false;
17
18 Node<T>* slowWalker = linkedList;
19 Node<T>* quickWalker = linkedList;
20 while(quickWalker != NULL && quickWalker->next != NULL)
21 {
22 // move the walker
23 slowWalker = slowWalker->next; //one each step
24 quickWalker = quickWalker->next->next; //two each step
25 if(slowWalker == quickWalker)
26 {
27 //has circle
28 *loopCross = slowWalker;
29 return true;
30 }
31 }
32
33 return false;
34 }
35
36 //Get the loop start node
37 template<typename T>
38 Node<T>* getLoopStart(Node<T>* linkedList, Node<T>* loopCross)
39 {
40 Node<T>* startFromHead = linkedList;
41 Node<T>* startFromCross = loopCross;
42 // Move one pointer from head and move another from the cross node.
43 // They will meet each other at the loop start node.
44 while(startFromHead != startFromCross)
45 {
46 startFromHead = startFromHead->next;
47 startFromCross = startFromCross->next;
48 }
49 return startFromHead;
50 }
51
52 int main()
53 {
54 Node<int>* linkedList = new Node<int>();
55 linkedList->value = 0;
56 linkedList->next = NULL;
57
58 Node<int>* pNode = linkedList;
59 Node<int>* crossNode = NULL;
60
61 for(int i = 1; i < 100; i++)
62 {
63 Node<int>* tem = new Node<int>();
64 tem->value = i;
65 tem->next = NULL;
66
67 pNode->next = tem;
68 pNode = tem;
69 // set the cross node;
70 if(i == 66)
71 crossNode = tem;
72 }
73
74 printf("test normal linked list:\n");
75 if(hasLoop(linkedList))
76 printf("has circle.\n");
77 else
78 printf("no circle.\n");
79
80 printf("test circle linked list:\n");
81 pNode->next = crossNode; // Create a circle
82
83 Node<int>* loopCross = NULL;
84 if(hasLoop(linkedList, &loopCross))
85 {
86 printf("has circle.\n");
87 Node<int>* loopStart = getLoopStart(linkedList, loopCross);
88 if(loopStart != NULL)
89 printf("the value of the circle start node is %d\n", loopStart->value);
90 }
91 else
92 printf("no circle.");
93 }。