一 单链表
1.1 概念
单链表:线性表的链式存储
线性表:一对一的关系
链式存储:不需要在内存中开辟一段连续的内存的空间,所以每一个数据不再是一个基本数据,而是由两部分组成,数据域和指针域,数据域保存数据,指针域保存下一个结点的地址。
单链表:就是单向链表,前者结点可以找到后者结点,但是后者无法找到前者。
1.2 单链表的操作
1.2.1 定义结点结构体
C
自动换行
#
ifndef
_LINKLIST_H_
#
define
_LINKLIST_H_
#
include
<stdio.h>
#
include
<stdlib.h>
//定义数据类型
typedef
int
DataType
;
//定义结点结构体
typedef
struct
linklist
{
DataType data
;
//数据域
struct
linklist
*
next
;
//指针域,为了能够操作后面结点
//所以指针的类型为当前结构体的类型
}
linklist
; endif
1.2.2 创建一个空的单链表
//创建一个空的单链表 linklist* LinkListCreate() { //定义一个头结点,在堆区开辟空间 linklist *head = (linklist *)malloc(sizeof(linklist)); //初始化指针域标识为空 head->next = NULL; return head; }
1.2.3 头插法插入数据
//头插法插入数据 void LinkListInsertHead(linklist *head,DataType value) { //开辟空间,分配一个新的结点 linklist *tmp = (linklist *)malloc(sizeof(linklist)); tmp->data = value; tmp->next = NULL; //将要插入的结点的指针域指向第一个结点 //第一个结点的地址:head->next //新插入结点的指针域:tmp->next tmp->next = head->next; //头结点的指针域保存要插入的结点的地址 //头结点的指针域:head->next //新插入结点的地址:tmp head->next = tmp; return; }
1.2.4 遍历单链表
//遍历单链表 void LinkListPrint(linklist *head) { //定义一个指针保存第一个结点的地址 linklist *p = head->next; while(p != NULL) { //打印数据 printf("%d ",p->data); //p指向下一个结点(p保存下一个结点的地址) p = p->next; } putchar(10); }
1.2.5 尾插法插入数据
//尾插法插入数据 void LinkListInsertTail(linklist *head,DataType value) { //申请空间,分配结点结构体 linklist *tmp = (linklist *)malloc(sizeof(linklist)); tmp->data = value; tmp->next = NULL; //找到最后一个结点 linklist *p = head; while(p->next != NULL) { p = p->next; } //将新插入的结点保存在最后一个结点的后面 p->next = tmp; //将新插入的结点的指针域指向NULL tmp->next = NULL; return; }
1.2.6 判断单链表是否为空
//判断单链表是否为空 int LinkListIsEmpty(linklist *head) { return head->next == NULL ? 1 : 0; }
1.2.7 头删法删除数据(返回删除的数据)
//头删法,返回删除的数据 DataType LinkListDeleteHead(linklist *head) { if(LinkListIsEmpty(head)) { printf("删除失败,单链表为空!\n"); return (DataType)-1; } DataType value = head->next->data; linklist *tmp = head->next; head->next = head->next->next; free(tmp); tmp = NULL; return value; }
1.2.8 按照数据修改数据
void LinkListUpdate(linklist *head,DataType OldValue,DataType NewValue) { if(LinkListIsEmpty(head)) { printf("修改数据失败,链表为空"); return; } linklist *p = head; int flags = 0; while(p->next != NULL) { p = p->next; if(p->data == OldValue) { p->data = NewValue; flags = 1; } } if(flags == 0) { printf("数据%d不存在,修改失败\n",OldValue); } return; }
1.2.9 按照位置查找数据
//按照位置查找数据 DataType LinklistSearchData(linklist *head,int pos) { if(LinkListIsEmpty(head)) { printf("查找失败,链表为空!\n"); } if(pos < 1) { printf("位置有误!\n"); } linklist *p = head; int i; for(i = 1; i <= pos;i++) { if(p == NULL) { printf("输入位置有误!\n"); return (DataType)-1; } p = p->next; } return p->data; }
1.2.10 按照数据查找位置
//按照数据查找位置 int LinkListSearchPos(linklist *head,DataType value) { if(LinkListIsEmpty(head)) { printf("查找位置失败,链表为空!\n"); return (DataType)-1; } linklist *p = head; int pos = 0; while(p->next != NULL) { p = p->next; pos++; if(p->data == value) { return pos; } } printf("查找位置失败!\n"); return (DataType)-1; }
1.2.11 按照位置插入数据
//按照位置插入数据 void LinkListInsertByPos(linklist *head,int pos,DataType value) { if(pos < 1) { printf("按照位置插入数据有误"); return; } linklist *tmp = (linklist *)malloc(sizeof(linklist)); tmp->data = value; tmp->next = NULL; if(LinkListIsEmpty(head)) { tmp->next = head->next; head->next = tmp; } else { int i; linklist *p = head; for(i = 0 ; i < pos ;i++) { if(p->next == NULL) { printf("位置有误!\n"); return; } p = p->next; } tmp->next = p->next; p->next = tmp; } }
1.2.12 直接插入排序
//直接插入排序 void LinkListInsertSort(linklist *head,DataType value) { linklist *tmp = (linklist *)malloc(sizeof(linklist)); tmp->data = value; tmp->next = NULL; linklist *p = head; while(p->next != NULL && p->next->data < tmp->data) { p = p->next; } tmp->next = p->next; p->next = tmp; }
练习:实现单链表的反转
h1: 10->20->30->40->NULL
......
h2:40->30->20->10->NULL
//单链表翻转 void LinkListReverse(linklist *head) { if(LinkListIsEmpty(head)) { printf("链表为空,无需翻转!\n"); return; } linklist *h1 = LinkListCreate(); linklist *p = head; DataType value; while(p->next != NULL) { value = LinkListDeleteHead(head); LinkListInsertHead(h1,value); } head->next= h1->next; } //单链表翻转2 void LinkListReverse2(linklist *head) { if(LinkListIsEmpty(head)) { printf("链表为空,无需翻转!\n"); return; } //定义两个指针变量来执行链表的翻转 linklist *p = NULL, *q = NULL; //p指针保存第一个结点的地址 p = head->next; //头结点指向NULL head->next = NULL; while(p != NULL) { //q指针保存p指向的结点的地址 q = p; p = p->next; //头插法将q插入到head链表中 q->next = head->next; head->next = q; } }
练习:单链表的整表删除
//单链表的整表删除 void LinkListClear(linklist *head) { linklist *p = NULL,*q = NULL; p = head->next; while(p != NULL) { q = p->next; free(p); p = q; } head->next = NULL; }
1.3 整体代码
//头文件 #ifndef _LINKLIST_H_ #define _LINKLIST_H_ #include <stdio.h> #include <stdlib.h> //定义数据类型 typedef int DataType; //定义结点结构体 typedef struct linklist { DataType data; //数据域 struct linklist *next; //指针域,为了能够操作后面结点 //所以指针的类型为当前结构体的类型 }linklist; //创建一个空的单链表 linklist* LinkListCreate(); //头插法插入数据 void LinkListInsertHead(linklist *head,DataType value); //遍历单链表 void LinkListPrint(linklist *head); //尾插法插入数据 void LinkListInsertTail(linklist *head,DataType value); //判断单链表是否为空 int LinkListIsEmpty(linklist *head); //头删法,返回删除的数据 DataType LinkListDeleteHead(linklist *head); //按照数据修改数据 void LinkListUpdate(linklist *head,DataType OldValue,DataType NewValue); //按照位置查找数据 DataType LinklistSearchData(linklist *head,int pos); //按照数据查找位置 int LinkListSearchPos(linklist *head,DataType value); //按照位置插入数据 void LinkListInsertByPos(linklist *head,int pos,DataType value); //直接插入排序 void LinkListInsertSort(linklist *head,DataType value); //单链表翻转 void LinkListReverse(linklist *head); //单链表翻转2 void LinkListReverse2(linklist *head); //单链表的整表删除 void LinkListClear(linklist *head); #endif
//功能函数 #include "linklist.h" //创建一个空的单链表 linklist* LinkListCreate() { //定义一个头结点,在堆区开辟空间 linklist *head = (linklist *)malloc(sizeof(linklist)); //初始化指针域标识为空 head->next = NULL; return head; } //头插法插入数据 void LinkListInsertHead(linklist *head,DataType value) { //开辟空间,分配一个新的结点 linklist *tmp = (linklist *)malloc(sizeof(linklist)); tmp->data = value; tmp->next = NULL; //将要插入的结点的指针域指向第一个结点 //第一个结点的地址:head->next //新插入结点的指针域:tmp->next tmp->next = head->next; //头结点的指针域保存要插入的结点的地址 //头结点的指针域:head->next //新插入结点的地址:tmp head->next = tmp; return; } //遍历单链表 void LinkListPrint(linklist *head) { //定义一个指针保存第一个结点的地址 linklist *p = head->next; while(p != NULL) { //打印数据 printf("%d ",p->data); //p指向下一个结点(p保存下一个结点的地址) p = p->next; } putchar(10); } //尾插法插入数据 void LinkListInsertTail(linklist *head,DataType value) { //申请空间,分配结点结构体 linklist *tmp = (linklist *)malloc(sizeof(linklist)); tmp->data = value; tmp->next = NULL; //找到最后一个结点 linklist *p = head; while(p->next != NULL) { p = p->next; } //将新插入的结点保存在最后一个结点的后面 p->next = tmp; //将新插入的结点的指针域指向NULL tmp->next = NULL; return; } //判断单链表是否为空 int LinkListIsEmpty(linklist *head) { return head->next == NULL ? 1 : 0; } //头删法,返回删除的数据 DataType LinkListDeleteHead(linklist *head) { if(LinkListIsEmpty(head)) { printf("删除失败,单链表为空!\n"); return (DataType)-1; } DataType value = head->next->data; linklist *tmp = head->next; head->next = head->next->next; free(tmp); tmp = NULL; return value; } //按照数据修改数据 void LinkListUpdate(linklist *head,DataType OldValue,DataType NewValue) { if(LinkListIsEmpty(head)) { printf("修改数据失败,链表为空"); return; } linklist *p = head; int flags = 0; while(p->next != NULL) { p = p->next; if(p->data == OldValue) { p->data = NewValue; flags = 1; } } if(flags == 0) { printf("数据%d不存在,修改失败\n",OldValue); } return; } //按照位置查找数据 DataType LinklistSearchData(linklist *head,int pos) { if(LinkListIsEmpty(head)) { printf("查找失败,链表为空!\n"); } if(pos < 1) { printf("位置有误!\n"); } linklist *p = head; int i; for(i = 1; i <= pos;i++) { if(p == NULL) { printf("输入位置有误!\n"); return (DataType)-1; } p = p->next; } return p->data; } //按照数据查找位置 int LinkListSearchPos(linklist *head,DataType value) { if(LinkListIsEmpty(head)) { printf("查找位置失败,链表为空!\n"); return (DataType)-1; } linklist *p = head; int pos = 0; while(p->next != NULL) { p = p->next; pos++; if(p->data == value) { return pos; } } printf("查找位置失败!\n"); return (DataType)-1; } //按照位置插入数据 void LinkListInsertByPos(linklist *head,int pos,DataType value) { if(pos < 1) { printf("按照位置插入数据有误"); return; } linklist *tmp = (linklist *)malloc(sizeof(linklist)); tmp->data = value; tmp->next = NULL; if(LinkListIsEmpty(head)) { tmp->next = head->next; head->next = tmp; } else { int i; linklist *p = head; for(i = 0 ; i < pos ;i++) { if(p->next == NULL) { printf("位置有误!\n"); return; } p = p->next; } tmp->next = p->next; p->next = tmp; } } //直接插入排序 void LinkListInsertSort(linklist *head,DataType value) { linklist *tmp = (linklist *)malloc(sizeof(linklist)); tmp->data = value; tmp->next = NULL; linklist *p = head; while(p->next != NULL && p->next->data < tmp->data) { p = p->next; } tmp->next = p->next; p->next = tmp; } //单链表翻转 void LinkListReverse(linklist *head) { if(LinkListIsEmpty(head)) { printf("链表为空,无需翻转!\n"); return; } linklist *h1 = LinkListCreate(); linklist *p = head; DataType value; while(p->next != NULL) { value = LinkListDeleteHead(head); LinkListInsertHead(h1,value); } head->next= h1->next; } //单链表翻转2 void LinkListReverse2(linklist *head) { if(LinkListIsEmpty(head)) { printf("链表为空,无需翻转!\n"); return; } //定义两个指针变量来执行链表的翻转 linklist *p = NULL, *q = NULL; //p指针保存第一个结点的地址 p = head->next; //头结点指向NULL head->next = NULL; while(p != NULL) { //q指针保存p指向的结点的地址 q = p; p = p->next; //头插法将q插入到head链表中 q->next = head->next; head->next = q; } } //单链表的整表删除 void LinkListClear(linklist *head) { linklist *p = NULL,*q = NULL; p = head->next; while(p != NULL) { q = p->next; free(p); p = q; } head->next = NULL; }
//main.c #include "linklist.h" int main(int argc, char const *argv[]) { linklist *head = LinkListCreate(); int i; for(i= 0 ; i < 10;i++) { LinkListInsertHead(head,i + 1); } LinkListPrint(head); for(; i < 20;i++) { LinkListInsertTail(head,i + 1); } LinkListPrint(head); for(i = 0 ; i < 5;i++) { LinkListDeleteHead(head); } LinkListPrint(head); LinkListUpdate(head,11,999); LinkListPrint(head); printf("位置4的数据为%d\n",LinklistSearchData(head,4)); printf("数据为999的位置为%d\n",LinkListSearchPos(head,9999)); LinkListInsertByPos(head,3,777); LinkListPrint(head); LinkListInsertSort(head,9); LinkListInsertSort(head,188); LinkListInsertSort(head,12); LinkListInsertSort(head,0); LinkListInsertSort(head,6); LinkListPrint(head); LinkListReverse(head); LinkListPrint(head); LinkListReverse2(head); LinkListPrint(head); LinkListClear(head); LinkListInsertSort(head,9); LinkListInsertSort(head,188); LinkListInsertSort(head,12); LinkListInsertSort(head,0); LinkListInsertSort(head,6); LinkListPrint(head); return 0; }
二 单向循环链表
2.1 概念
2.2 单向循环链表的操作
定义结点结构体
创建一个空的单向循环链表
插入数据
打印数据
删除头结点
打印数据
2.2.1 定义结点结构体
//定义数据类型 typedef int DataType; //定义结点结构体 typedef struct looplist { DataType data; struct looplist *next; }looplist;
2.2.2 创建空的单向循环链表
//创建一个空的单向循环链表 looplist* LoopListCreate() { looplist *head = (looplist *)malloc(sizeof(looplist)); //初始状态就为循环 //头结点的指针域保存头结点的地址 head->next = head; return head; }
2.2.3 插入数据
//插入数据 void LoopListInsert(looplist *head,DataType value) { looplist *tmp = (looplist *)malloc(sizeof(looplist)); tmp->next = NULL; tmp->data = value; tmp->next = head->next; head->next = tmp; return; }
2.2.4 遍历单向循环链表
//遍历单向循环链表 void LoopListPrint(looplist *head) { looplist *p = head; while(p->next != head) { p = p->next; printf("%d ",p->data); } putchar(10); }
2.2.5 删除头结点
//删除头结点 looplist* LoopListCutHead(looplist *head) { looplist *p = head; while(p->next != head) { p = p->next; } p->next = head->next; free(head); head = NULL; return p->next; }
2.2.6 去头结点遍历
//去头结点遍历单向循环链表 void LoopListPrint2(looplist *head) { looplist *p = head; while(p->next != head) { printf("%d ",p->data); p = p->next; } printf("%d\n",p->data); }
2.3 作业 joseph问题
设编号分别为:1,2...n个人围成一圈,约定序号为k(1
例如:n = 8,k = 3,m = 4
void Joseph(int n,int k,int m) { if(n < k || n < m) { printf("k或者M输入有误!,k = %d,m = %d\n",k,m); return; } //创建一个空的单向循环链表 looplist *h1 = LoopListCreate(); int i; for(i = n ; i >=1;i--) { LoopListInsert(h1,i); } h1 = LoopListCutHead(h1); LoopListPrint2(h1); //找到编号为k的人 for(i = 1; i < k;i++) { h1 = h1->next; } //循环删除数到m的那个结点 looplist *tmp = NULL; while(h1->next != h1) { //为了删除指定的结点,所以h1指针保存数到m-1的结点 for(i = 1; i < m-1;i++) { h1 = h1->next; } //删除h1后面的结点 tmp = h1->next; h1->next = tmp->next; printf("%d ",tmp->data); free(tmp); tmp = NULL; //从下一个结点开始数数 h1 = h1->next; } printf("%d\n",h1->data); free(h1); h1 = NULL; return; }
2.4 完整代码
looplist.h #ifndef _LOOPLIST_H_ #define _LOOPLIST_H_ #include <stdio.h> #include <stdlib.h> //定义数据类型 typedef int DataType; //定义结点结构体 typedef struct looplist { DataType data; struct looplist *next; }looplist; //创建一个空的单向循环链表 looplist* LoopListCreate(); //插入数据 void LoopListInsert(looplist *head,DataType value); //遍历单链表 void LoopListPrint(looplist *head); //删除头结点 looplist* LoopListCutHead(looplist *head); //去头结点遍历单向循环链表 void LoopListPrint2(looplist *head); #endif
//main.c #include "looplist.h" int main(int argc, char const *argv[]) { looplist *head = LoopListCreate(); int i; for(i = 0 ; i < 10;i++) { LoopListInsert(head,i + 1); } LoopListPrint(head); head = LoopListCutHead(head); LoopListPrint2(head); return 0; }
//looplist.c #include "looplist.h" //创建一个空的单向循环链表 looplist* LoopListCreate() { looplist *head = (looplist *)malloc(sizeof(looplist)); //初始状态就为循环 //头结点的指针域保存头结点的地址 head->next = head; return head; } //插入数据 void LoopListInsert(looplist *head,DataType value) { looplist *tmp = (looplist *)malloc(sizeof(looplist)); tmp->next = NULL; tmp->data = value; tmp->next = head->next; head->next = tmp; return; } //遍历单向循环链表 void LoopListPrint(looplist *head) { looplist *p = head; while(p->next != head) { p = p->next; printf("%d ",p->data); } putchar(10); } //删除头结点 looplist* LoopListCutHead(looplist *head) { looplist *p = head; while(p->next != head) { p = p->next; } p->next = head->next; free(head); head = NULL; return p->next; } //去头结点遍历单向循环链表 void LoopListPrint2(looplist *head) { looplist *p = head; while(p->next != head) { printf("%d ",p->data); p = p->next; } printf("%d\n",p->data); }