大纲:
单向循环链表
- 创建
- 循环遍历
- 插入
- 删除
- 查找
循环链表
一、单向循环链表
循环链表中没有首结点,都是首元结点
#include <stdio.h>
#include "string.h"
#include "ctype.h"
#include "stdlib.h"
#include "math.h"
#include "time.h"
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OK 1
#define MAXSIZE 20 //存储空间初始分配量
typedef int Status;//Status 是函数的类型,其值是函数结果状态代码l,如OK等
typedef int ElemType;//ElemType 类型根据实际情况而定,这里假设为int
//定义结点
typedef struct Node{
ElemType data;
struct Node *next;
}Node;
typedef struct Node * LinkList;
1、循环链表的初始化及赋值
有两种情况:1、第一次创建 2、已经创建
(1)、第一次创建,只有一个L指针的时候,还没有任何结点
(2)、已经有结点之后的插入
方法1
/*
1、创建单向循环链表 方法1
判断是否是第一次创建链表
YES ->创建一个新结点,并使得新结点的next 指向自身; (*L)->next = (*L);
NO ->找链表尾结点,将尾结点的next = 新结点。新结点的next = (*L)
*/
//LinkList *L 为什么要 带* 。当需要修改其本身的时候需要加 *,当不需要修改其本身的时候,例如打印就不需要*
//单链表找尾结点,是根据尾结点的next 为NUll(空)。单向循环链表的尾结点指向 L
Status CreateList(LinkList *L){
int item;
LinkList temp = NULL;
LinkList target = NULL;
printf("输入结点的值,输入0结束\n");
while (1) {
scanf("%d",&item);//用item 接收输入的数字
if (item==0) break;//如果输入的是0 输入结束
//如果输入的链表是空。则创建一个新的结点,使其next 指针指向自己 (*head)->next = *head;
if (*L == NULL) {
*L = (LinkList)malloc(sizeof(Node));//LinkList 是可以不加的,但是养成代码习惯还是要加上的
if(!L) exit(0);//退出
(*L)->data = item;//将输入的值存入结点
(*L)->next = *L;//将结点指向自己
}else{
//输入的链表不是空的,寻找链表的尾结点,使尾结点的next=新结点。新结点的next指向头结点
//1、找到最后一个结点
//从tatget = *L 第一个位置开始遍历。
//如果 tatget->next = *L 的时候就是尾结点了,所以 != *L 的话继续循环
// != L 的时候将tatget赋值从下一个结点( tatget->next 指向下一个结点),继续遍历,直到 tatget->next = *L 的时候,这个时候的tatget就是尾结点
for (target = *L; target->next != *L; target = target->next);
//2、创建新结点
temp = (LinkList)malloc(sizeof(Node));
if (!temp) return ERROR;
//3、向新创建的结点中存储数据
temp->data = item;
temp->next = *L;
target->next = temp;//在尾结点后面插入新创建的结点
}
}
return OK;
}
方法2
/*
2、创建单向循环链表 方法2
方法1 是通过循环遍历的方式找到尾结点。我们也可以用一个指针一直指向尾结点,在插入数据的时候就方便找到尾结点
*/
Status CreateList2(LinkList *L){
int item;
LinkList temp = NULL;
LinkList r = NULL;
printf("输入结点的值,输入0结束\n");
while (1) {
scanf("%d",&item);//用item 接收输入的数字
if (item==0) break;//如果输入的是0 输入结束
//如果输入的链表是空。则创建一个新的结点,使其next 指针指向自己 (*head)->next = *head;
if (*L == NULL) {
*L = (LinkList)malloc(sizeof(Node));//LinkList 是可以不加的,但是养成代码习惯还是要加上的
if(!L) exit(0);//退出
(*L)->data = item;//将输入的值存入结点
(*L)->next = *L;//将结点指向自己
r = *L;// r 指向 *L
}else{
//输入的链表不是空的,r 指向尾结点,使尾结点的next=新结点。新结点的next指向头结点
//2、创建新结点
temp = (LinkList)malloc(sizeof(Node));
if (!temp) return ERROR;
//3、向新创建的结点中存储数据
temp->data = item;
temp->next = *L;
r->next = temp;//在尾结点后面插入新创建的结点
r = temp;//r 指向最新添加到链表中的结点,也就是最后一个结点
}
}
return OK;
}
结果是一样的,所以就写一种的输出
int main(int argc, const char * argv[]) {
// insert code here...
printf("Hello, World!\n");
LinkList head;
int place,num;
int iStatus;
// iStatus = CreateList(&head);//通过遍历查找尾结点
iStatus = CreateList2(&head);//通过一个指针指向尾结点方便插入
printf("原始的链表:\n");
ListTraverse(head);
//******************************************************
输入结点的值,输入0结束
4
7
3
8
0
原始的链表:
4 7 3 8
2、遍历链表
//2、遍历一下链表,循环链表的遍历最好用 do while 语句,因为头结点就有值
void ListTraverse(LinkList p)
{
//如果链表是空
if (p == NULL) {
printf("打印的链表为空!\n");
return;
}else{
LinkList temp;
temp = p;//从第一个结点开始
do {
printf("%5d",temp->data);
temp = temp->next;//下一个结点
} while (temp != p);//如果等于p,循环完毕不再执行
printf("\n");
}
}
3、插入数据
(1)、插入位置在首元结点上
1)、判断插入位置是否在首元结点上
2)、创建新结点,并复制给新结点
插入过程:
1、将新创建的结点 next 指向原来的首元结点
2、将尾结点 next 指向新创建的结点
3、将L指针指向新创建的结点
(2)、其他位置插入
例如: a b c d 在 b c中间插入e
1)、创建新结点(target e),并复制给新结点
2)、找到插入的位置的前一个结点(b)
插入过程:
1、找到插入结点前一个结点(b)
2、将新创建结点的next 指向插入位置后面的结点(e->next = c)
3、然后再把插入位置前面的结点next 指向新创建的结点(b->next= e)
//3、循环链表插入
Status ListInsert(LinkList *L, int place, int num){
LinkList temp,target;//创建辅助变量
int i;
if (place == 1) {
// 如果插入的位置为1,则属于插入首元结点,所以需要特殊处理
// 1、创建新结点temp,并判断是否创建成功,成功则赋值,否则返回 ERROR;
// 2、找到链表中最后的结点 - 尾结点;
// 3、让新结点的next 指向头结点;
// 4、尾结点的next 指向新的头结点;
// 5、让头指针 - L指针 指向temp(临时的新结点)
temp = (LinkList)malloc(sizeof(Node));
if (temp == NULL) return ERROR;
temp->data = num;//给新创建的结点赋值
// 找到尾结点,让其指向新的首元结点
for (target = *L; target->next != *L; target = target->next);
temp->next = *L;
target->next = temp;
*L = temp;
}else{
// 如果插入的位置在其他位置
// 1、创建新结点temp,并判断是否创建成功,成功则赋值,否者返回 ERROR
// 2、先找到插入的位置,如果超过链表长度,则自动插入队尾;
// 3、通过target 找到要插入位的前一个结点,让target->next = temp;
// 4、插入结点的前驱指向新结点,新结点的next 指向target 原来的next 位置;
temp = (LinkList)malloc(sizeof(Node));
if (temp == NULL) return ERROR;
temp->data = num;//新结点赋值
//查找插入位置的前一个结点。如果超过链表长度插到最后
//i=1, target = *L 从第一个节点开始遍历
//target->next != *L 不是最后一个结点 && i != place-1 不是需要插入的位置请一个结点位置
//target = target->next target 赋值成下一个结点继续判断 同时 i++,正在查看的位置向后移动1位
for (i=1,target = *L; target->next != *L && i != place-1; target = target->next, i++);
temp->next = target->next;//新结点指向插入位置前一个结点所指向的结点 (E->next = B->next (C))
target->next = temp;//插入位置前一个结点指向新创建的结点 (B->next = E)
}
return OK;
}
printf("请输入要插入的数据以及位置用空格隔开:");
scanf("%d %d",&place,&num);
iStatus = ListInsert(&head, place, num);
ListTraverse(head);
//*************************************************
原始的链表:
4 7 3 8
请输入要插入的数据以及位置用空格隔开:2 99
4 99 7 3 8
循环链表查询值:
4、删除
这里删除返回的是1 和 0 代表删除成功或失败,也可以将删除的值返回
//4、循环链表的删除
Status LinkListDelete(LinkList *L, int place){
LinkList temp, target;
int i;
// temp指向首元结点
temp = *L;
if (temp == NULL || place < 1) return ERROR;
//删除的是首元结点
if (place == 1) {
//1、如果删除的只剩下首元结点,则直接将 *L置空
if ((*L)->next == (*L)) {
(*L)->next = NULL;
return OK;
}
//2、链表还有其他数据,删除首元结点的时候
//(1)、找到尾结点,使尾结点next 指向头结点的下一个结点 target->next = (*L)->next;
//(2)、新结点作为头结点,则释放原来的头结点
for (target = *L; target->next != *L; target = target->next);
temp = *L;
*L= (*L)->next;
target->next = *L;
free(temp);
}else{
//如果删除其他结点
//1、找到删除结点前一个结点target
//2、使得target->next 指向下一个结点
//3、释放需要删除的结点temp
for (i=0,target = *L; target->next != *L && i != place-1; target = target->next, i++);
// if (target->next == *L && i != 0) return ERROR;//超出链表长度的值的时候
if (place > i) return ERROR;//
temp = target->next;
target->next = temp->next;
free(temp);
}
return OK;
}
printf("循环链表删除值:");
scanf("%d",&place);
num = LinkListDelete(&head, place);
printf("\n删除(1 成功, 0 失败) %d\n",num);
ListTraverse(head);
//********************************************
3 99 4 2
循环链表删除值:1
删除(1 成功, 0 失败) 1
99 4 2
5、查询
//5、循环链表查询值 的位置
int findValue(LinkList L,int value){
int i = 1;
LinkList p;
p = L;
// 寻找链表中的结点 data = value
// 循环 p->data 不是我们要找的,同时 还没有找到最后的时候 i++继续下一个
while (p->data != value && p->next != L) {
i++;
p = p->next;
}
// 遍历完之后,如果p 指针指向第一个结点,同时data != value 返回-1,也就是没找到。找到了就返回对应的位置
if(p->next == L && p->data != value) return -1;
return i;
}
printf("循环链表查询值:");
scanf("%d",&num);
place = findValue(head, num);
if (place == -1) {
printf("链表中没有您所查询的值!\n");
}else
printf("查询到值的位置:%d \n",place);
ListTraverse(head);
//*************************************
3 99 4 2
循环链表查询值:4
查询到值的位置:3
3 99 4 2