线性表基本概念
线性表是零个或者多个数据元素的有限序列。
特性:
数据元素之间是有顺序的.
数据元素个数是有限的.
数据元素的类型必须相同.
性质:
a0为线性表的第一个元素,只有一个后继。
an为线性表的最后一个元素,只有一个前驱。
除a0 和an外的其它元素ai,既有前驱,又有后继。
线性表能够逐项访问和顺序存取。
线性表的顺序存储
线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。
优点:
无需为线性表中的逻辑关系增加额外的空间。
可以快速的获取表中合法位置的元素。
缺点:
插入和删除操作需要移动大量元素。
当线性表长度变化较大的时候,难以确定存储空间的容量。
操作要点:
插入元素算法
判断线性表是否合法
判断插入位置是否合法
把最后一个元素到插入位置的元素后移一个位置
将新元素插入
线性表长度加1
获取元素操作
判断线性表是否合法
判断位置是否合法
直接通过数组下标的方式获取元素
删除元素算法
判断线性表是否合法
判断删除位置是否合法
将元素取出
将删除位置后的元素分别向前移动一个位置
线性表长度减1
线性表顺序存储案例(动态数组)
//DynamicArray.h
#ifndef DYNAMIC_ARRAY_H
#define DYNAMIC_ARRAY_H
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
//动态增长内存,策略 将存放数据的内存放到那?堆上
//动态数组 如果5个元素 申请内存 拷贝数据 释放内存6 插入第七个?
//容量capacity表示我的这块内存空间一共可以存放多少元素
//size概念 记录当前数组中具体的元素个数
//动态数组的结构体定义完了
typedef struct DYNAMICARRAY{
int* pAddr; //存放数据的地址
int size; //当前有多少个元素
int capacity; //容量,我容器当前最大能容纳多少元素
}Dynamic_Array;
//写一系列的相关对DYNAMICARRAY结构体操作的函数
//初始化
Dynamic_Array* Init_Array();
//插入
void PushBack_Array(Dynamic_Array* arr,int value);
//向指定的位置插入元素
int InsertByPos_Array(Dynamic_Array* arr,int pos,int value);
//根据位置删除
void RemoveByPos_Array(Dynamic_Array* arr,int pos);
//根据值删除
void RemoveByValue_Array(Dynamic_Array* arr, int value);
//查找
int Find_Array(Dynamic_Array* arr, int value);
//打印
void Print_Array(Dynamic_Array* arr);
//释放动态数组的内存
void FreeSpace_Array(Dynamic_Array* arr);
//清空数组
void Clear_Array(Dynamic_Array* arr);
//获得动态数组容量
int Capacity_Array(Dynamic_Array* arr);
//获得动态数据当前元素个数
int Size_Array(Dynamic_Array* arr);
//根据位置获得某个位置元素
int At_Array(Dynamic_Array* arr,int pos);
#endif
//DynamicArray.c
#include"DynamicArray.h"
//动态数组的初始化
Dynamic_Array* Init_Array(){
//申请内存
Dynamic_Array* myArray = (Dynamic_Array*)malloc(sizeof(Dynamic_Array)); //给数组结构体分配内存
//初始化
myArray->size = 0;
myArray->capacity = 20;
myArray->pAddr = (int*)malloc(sizeof(int)*myArray->capacity); //初始化分配内存20个int类型长度
return myArray;
}
//插入,默认数组尾部插入
void PushBack_Array(Dynamic_Array* arr, int value){
if (arr == NULL){
return;
}
//判断空间是否足够
if (arr->size == arr->capacity){
//第一步 申请一块更大的内存空间 新空间是旧空间的2倍
int* newSpace = (int*)malloc(sizeof(int) * arr->capacity * 2);
//第二步 拷贝数据到新的空间
memcpy(newSpace,arr->pAddr,arr->capacity * sizeof(int));
//第三步 释放旧空间的内存
free(arr->pAddr);
//更新容量
arr->capacity = arr->capacity * 2;
arr->pAddr = newSpace;
}
//插入新元素
arr->pAddr[arr->size] = value;
arr->size++;
}
//向指定的位置插入元素
int InsertByPos_Array(Dynamic_Array* arr, int pos, int value)
{
if (arr == NULL) {
return -1;
}
//判断位置是否有效
if (pos < 0 || pos >= arr->size) {
return -1;
}
if (arr->size == arr->capacity) {
//第一步 申请一块更大的新空间
int* newSpace = (int*)malloc((sizeof(int)* arr->capacity) * 2);
//第二部 拷贝数据到新空间
memcpy(newSpace, arr->pAddr, sizeof(int)*arr->capacity);
//第三步 释放久空间
free(arr->pAddr);
//更新容量
arr->capacity = arr->capacity * 2;
arr->pAddr = newSpace;
}
//插入元素
//第一步 移动元素
for (int i = arr->size - 1; i >= pos; i--) {
arr->pAddr[i + 1] = arr->pAddr[i];
}
//第二步 在pos位置插入元素
arr->pAddr[pos] = value;
arr->size++;
return 0;
}
//根据位置删除
void RemoveByPos_Array(Dynamic_Array* arr, int pos){
if (arr == NULL){
return;
}
//判断位置是否有效
if(pos < 0 || pos >= arr->size){
return;
}
//删除元素
for (int i = pos; i < arr->size -1; i ++){
arr->pAddr[i] = arr->pAddr[i + 1];
}
arr->size--;
}
//根据值删除value第一次出现的位置
void RemoveByValue_Array(Dynamic_Array* arr, int value){
if (arr == NULL){
return;
}
//找到值的位置
int pos = Find_Array(arr,value);
//根据位置删除
RemoveByPos_Array(arr, pos);
}
//查找
int Find_Array(Dynamic_Array* arr, int value){
if (arr == NULL){
return -1;
}
//找到值的位置
int pos = -1;
for (int i = 0; i < arr->size; i++){
if (arr->pAddr[i] == value){
pos = i;
break;
}
}
return pos;
}
//打印
void Print_Array(Dynamic_Array* arr){
if (arr == NULL){
return;
}
for (int i = 0; i < arr->size;i++){
printf("%d ",arr->pAddr[i]);
}
printf("\n");
}
//释放动态数组的内存
void FreeSpace_Array(Dynamic_Array* arr){
if (arr == NULL){
return;
}
if (arr->pAddr != NULL){
free(arr->pAddr);
}
free(arr);
}
//清空数组
void Clear_Array(Dynamic_Array* arr){
if (arr == NULL){
return;
}
//pAddr -> 空间
arr->size = 0;
}
//获得动态数组容量
int Capacity_Array(Dynamic_Array* arr){
if (arr == NULL){
return -1;
}
return arr->capacity;
}
//获得动态数据当前元素个数
int Size_Array(Dynamic_Array* arr){
if (arr == NULL){
return -1;
}
return arr->size;
}
//根据位置获得某个位置元素
int At_Array(Dynamic_Array* arr, int pos){
return arr->pAddr[pos];
}
//动态数组main.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include"DynamicArray.h"
void test01(){
//初始化动态数组
Dynamic_Array* myArray = Init_Array();
//打印容量
printf("数组容量:%d\n", Capacity_Array(myArray));
printf("数组大小:%d\n", Size_Array(myArray));
//插入元素
for (int i = 0; i < 30;i++){
PushBack_Array(myArray, i);
}
//指定位置插入元素
InsertByPos_Array(myArray,5,55);
printf("数组容量:%d\n", Capacity_Array(myArray));
printf("数组大小:%d\n", Size_Array(myArray));
//打印
Print_Array(myArray);
//删除
RemoveByPos_Array(myArray,0);
RemoveByValue_Array(myArray,27);
//打印
Print_Array(myArray);
//查找5个位置
int pos = Find_Array(myArray, 5);
printf("5查找到:pos:%d %d\n", pos, At_Array(myArray, pos));
//销毁
FreeSpace_Array(myArray);
}
int main(void){
test01();
system("pause");
return 0;
}
线性表的链式存储
线性表的链式存储结构中,每个节点中只包含一个指针域,这样的链表叫 单链表。
通过每个节点的指针域将线性表的数据元素按其逻辑次序链接在一起
优点:
无需一次性定制链表的容量
插入和删除操作无需移动数据元素
缺点:
数据元素必须保存后继元素的位置信息
获取指定数据的元素操作需要顺序访问之前的元素
单向链表
头插法
新节点指向当前的第一个结点
尾结点指向新节点
第一次插入结点
尾节点指针指向第一个数据节点(即自己指向自己)
删除普通结点
删除头结点
更新head结点,并且尾结点重新连接新的头结点
//LinkList.h
#ifndef LINKLIST_H
#define LINKLIST_H
#include<stdlib.h>
#include<stdio.h>
//链表结点
typedef struct LINKNODE{
void* data; //指向任何类型的数据
struct LINKNODE* next;
}LinkNode;
//链表结构体
typedef struct LINKLIST{
LinkNode* head;
int size;
//需要容量吗?链表没有容量的概念
}LinkList;
//打印函数指针
typedef void(*PRINTLINKNODE)(void*);
//初始化链表
LinkList* Init_LinkList();
//指定位置插入
void Insert_LinkList(LinkList* list,int pos,void* data);
//删除指定位置的值
void RemoveByPos_LinkList(LinkList* list, int pos);
//获得链表的长度
int Size_LinkList(LinkList* list);
//查找
int Find_LinkList(LinkList* list,void* data);
//返回第一个结点
void* Front_LinkList(LinkList* list);
//打印链表结点
void Print_LinkList(LinkList* list, PRINTLINKNODE print);
//释放链表内存
void FreeSpace_LinkList(LinkList* list);
#endif
//LinkList.c
#include"LinkList.h"
//初始化链表
LinkList* Init_LinkList(){
LinkList* list = (LinkList*)malloc(sizeof(LinkList));
list->size = 0;
//头结点 是不保存数据信息
list->head = (LinkNode*)malloc(sizeof(LinkNode));
list->head->data = NULL;
list->head->next = NULL;
return list;
}
//指定位置插入
void Insert_LinkList(LinkList* list, int pos, void* data){
if (list == NULL){
return;
}
if (data == NULL){
return;
}
//友好的处理,pos越界
if (pos < 0 || pos > list->size){
pos = list->size;
}
//创建新的结点
LinkNode* newnode = (LinkNode*)malloc(sizeof(LinkNode));
newnode->data = data;
newnode->next = NULL;
//找结点
//辅助指针变量
LinkNode* pCurrent = list->head;
for (int i = 0; i < pos;i++){
pCurrent = pCurrent->next;
}
//新结点入链表
newnode->next = pCurrent->next;
pCurrent->next = newnode;
list->size++;
}
//删除指定位置的值
void RemoveByPos_LinkList(LinkList* list, int pos){
if (list == NULL){
return;
}
if (pos < 0 || pos >= list->size){
return;
}
//查找删除结点的前一个结点
LinkNode* pCurrent = list->head;
for (int i = 0; i < pos;i ++){
pCurrent = pCurrent->next;
}
//缓存删除的结点
LinkNode* pDel = pCurrent->next;
pCurrent->next = pDel->next;
//释放删除结点的内存
free(pDel);
list->size--;
}
//获得链表的长度
int Size_LinkList(LinkList* list){
return list->size;
}
//查找
int Find_LinkList(LinkList* list, void* data){
if (list == NULL){
return -1;
}
if (data == NULL){
return -1;
}
//遍历查找
LinkNode* pCurrent = list->head->next;
int i = 0;
while (pCurrent != NULL){
if (pCurrent->data == data){
break;
}
i++;
pCurrent = pCurrent->next;
}
return i;
}
//返回第一个结点
void* Front_LinkList(LinkList* list){
return list->head->next->data;
}
//打印链表结点
void Print_LinkList(LinkList* list, PRINTLINKNODE print){
if (list == NULL){
return;
}
//辅助指针变量
LinkNode* pCurrent = list->head->next;
while (pCurrent != NULL){
print(pCurrent->data);
pCurrent = pCurrent->next;
}
}
//释放链表内存
void FreeSpace_LinkList(LinkList* list){
if (list == NULL){
return;
}
//辅助指针变量
LinkNode* pCurrent = list->head;
while (pCurrent != NULL){
//缓存下一个结点
LinkNode* pNext = pCurrent->next;
free(pCurrent);
pCurrent = pNext;
}
//释放链表内存
list->size = 0;
free(list);
}
//单向链表main.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "LinkList.h"
//自定义数据类型
typedef struct PERSON{
char name[64];
int age;
int score;
}Person;
//打印函数
void MyPrint(void* data){
Person* p = (Person*)data; //转类型
printf("Name:%s Age:%d Score:%d\n",p->name,p->age,p->score);
}
int main(void){
//创建链表
LinkList* list = Init_LinkList();
//创建数据
Person p1 = { "aaa", 18, 100};
Person p2 = { "bbb", 19, 99 };
Person p3 = { "ccc", 20, 101 };
Person p4 = { "ddd", 17, 97 };
Person p5 = { "eee", 16, 59 };
//数据插入链表
Insert_LinkList(list, 0, &p1);
Insert_LinkList(list, 0, &p2);
Insert_LinkList(list, 0, &p3);
Insert_LinkList(list, 0, &p4);
Insert_LinkList(list, 0, &p5);
//打印
Print_LinkList(list, MyPrint);
//删除3
RemoveByPos_LinkList(list, 3);
//打印
printf("---------------\n");
Print_LinkList(list, MyPrint);
//返回第一个结点
printf("-----查找结果------------\n");
Person* ret = (Person*)Front_LinkList(list);
printf("Name:%s Age:%d Score:%d\n", ret->name, ret->age, ret->score);
//销毁链表
FreeSpace_LinkList(list);
system("pause");
return 0;
}
企业链表(小挂钩)
//企业链表LinkList.h
#ifndef LINKLIST_H
#define LINKLIST_H
#include<stdlib.h>
#include<stdio.h>
//链表小结点
typedef struct LINKNODE{
struct LINKNODE* next;
}LinkNode;
//链表结构体
typedef struct LINKLIST{
LinkNode head; //直接放结点本身,不是指针,在初始化时就不需要malloc内存给结点,
//在插入新结点时,也不需要malloc内存给新结点
int size;
}LinkList;
//遍历函数指针
typedef void(*PRINTNODE)(LinkNode*);
//比较函数指针
typedef int(*COMPARENODE)(LinkNode*, LinkNode*);
//初始化链表
LinkList* Init_LinkList();
//插入
void Insert_LinkList(LinkList* list, int pos, LinkNode* data);
//删除
void Remove_LinkList(LinkList* list, int pos);
//查找
int Find_LinkList(LinkList* list, LinkNode* data, COMPARENODE compare);
//返回链表大小
int Size_LinkList(LinkList* list);
//打印
void Print_LinkList(LinkList* list, PRINTNODE print);
//释放链表内存
void FreeSpace_LinkList(LinkList* list);
#endif
//企业链表LinkList.c
#include"LinkList.h"
//初始化链表
LinkList* Init_LinkList(){
LinkList* list = (LinkList*)malloc(sizeof(LinkList));
list->head.next = NULL;
list->size = 0;
return list;
}
//插入
void Insert_LinkList(LinkList* list, int pos, LinkNode* data){
if (list == NULL){
return;
}
if (data == NULL){
return;
}
if (pos < 0 || pos > list->size){
pos = list->size;
}
//查找插入位置
LinkNode* pCurrent = &(list->head); //这里head是实体,不是指针,所以要取地址&
for (int i = 0; i < pos;i++){
pCurrent = pCurrent->next;
}
//插入新节点
data->next = pCurrent->next;
pCurrent->next = data;
list->size++;
}
//删除
void Remove_LinkList(LinkList* list, int pos){
if (list == NULL){
return;
}
if (pos < 0 || pos >= list->size){
return;
}
//辅助指针变量
LinkNode* pCurrent = &(list->head); //这里head是实体,不是指针,所以要取地址&
for (int i = 0; i < pos;i++){
pCurrent = pCurrent->next;
}
//删除结点
//pCurrent->next = pCurrent->next->next;
LinkNode* pNext = pCurrent->next;
pCurrent->next = pNext->next;
free(pNext);
list->size--;
}
//查找
int Find_LinkList(LinkList* list, LinkNode* data, COMPARENODE compare){
if (list == NULL){
return -1;
}
if (data == NULL){
return -1;
}
//赋值指针变量
LinkNode* pCurrent = list->head.next;
int index = 0;
int flag = -1; //如没有匹配的,返回-1
while (pCurrent != NULL ){
if (compare(pCurrent, data) == 0){
flag = index;
break;
}
pCurrent = pCurrent->next;
index++;
}
return flag;
}
//返回链表大小
int Size_LinkList(LinkList* list){
return list->size;
}
//打印
void Print_LinkList(LinkList* list, PRINTNODE print){
if (list == NULL){
return;
}
//辅助指针
LinkNode* pCurrent = list->head.next;
while (pCurrent != NULL){
print(pCurrent);
pCurrent = pCurrent->next;
}
}
//释放链表内存
void FreeSpace_LinkList(LinkList* list){
if (list == NULL){
return;
}
free(list);
}
//企业链表main.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "LinkList.h"
typedef struct PERSON{
LinkNode node;
char name[64];
int age;
}Person;
void MyPrint(LinkNode* data){
Person* p = (Person*)data;
printf("Name:%s Age:%d\n",p->name,p->age);
}
int MyCompare(LinkNode* node1, LinkNode* node2){
Person* p1 = (Person*)node1; //改变指针类型,作用域不同
Person* p2 = (Person*)node2;
if (strcmp(p1->name,p2->name) == 0 && p1->age == p2->age){
return 0;
}
return -1;
}
int main(void){
//创建链表
LinkList* list = Init_LinkList();
//创建数据
Person p1, p2, p3, p4, p5;
strcpy(p1.name, "aaa");
strcpy(p2.name, "bbb");
strcpy(p3.name, "ccc");
strcpy(p4.name, "ddd");
strcpy(p5.name, "eee");
p1.age = 10;
p2.age = 20;
p3.age = 30;
p4.age = 40;
p5.age = 50;
//将结点插入链表
Insert_LinkList(list, 0, (LinkNode*)&p1);
Insert_LinkList(list, 0, (LinkNode*)&p2);
Insert_LinkList(list, 0, (LinkNode*)&p3);
Insert_LinkList(list, 0, (LinkNode*)&p4);
Insert_LinkList(list, 0, (LinkNode*)&p5);
//打印
Print_LinkList(list, MyPrint);
//删除结点
Remove_LinkList(list, 2);
//打印
printf("---------------\n");
Print_LinkList(list, MyPrint);
//查找
Person findP;
strcpy(findP.name, "bbb");
findP.age = 20;
int pos = Find_LinkList(list, (LinkNode*)&findP, MyCompare);
printf("位置:%d\n",pos);
//释放链表内存
FreeSpace_LinkList(list);
system("pause");
return 0;
}
循环链表
链表中最后一个结点的指针域指向头结点,整个链表形成一个环
优点
功能增强了(循环链表只是在单链表的基础上做了一个加强)
循环链表可以完全取代单链表的使用
循环链表的Next和Current操作可以高效的遍历链表中的所有元素
缺点
代码复杂度提高了
//CircleLinkList.h
#ifndef CIRCLELINKLIST
#define CIRCLELINKLIST
#include<stdio.h>
#include<stdlib.h>
//链表的小结点
typedef struct CIRCLELINKNODE{
struct CIRCLELINKNODE* next;
}CircleLinkNode;
//链表结构体
typedef struct CIRCLELINKLIST{
CircleLinkNode head;
int size;
}CircleLinkList;
//编写针对链表结构体操作的API函数
#define CIRCLELINKLIST_TRUE 1
#define CIRCLELINKLIST_FALSE 0
//比较回调
typedef int(*COMPARENODE)(CircleLinkNode*, CircleLinkNode*);
//打印回调
typedef void(*PRINTNODE)(CircleLinkNode*);
//初始化函数
CircleLinkList* Init_CircleLinkList();
//插入函数
void Insert_CircleLinkList(CircleLinkList* clist, int pos, CircleLinkNode* data);
//获得第一个元素
CircleLinkNode* Front_CircleLinkList(CircleLinkList* clist);
//根据位置删除
void RemoveByPos_CircleLinkList(CircleLinkList* clist,int pos);
//根据值去删除
void RemoveByValue_CircleLinkList(CircleLinkList* clist, CircleLinkNode* data, COMPARENODE compare);
//获得链表的长度
int Size_CircleLinkList(CircleLinkList* clist);
//判断是否为空
int IsEmpty_CircleLinkList(CircleLinkList* clist);
//查找
int Find_CircleLinkList(CircleLinkList* clist, CircleLinkNode* data, COMPARENODE compare);
//打印节点
void Print_CircleLinkList(CircleLinkList* clist, PRINTNODE print);
//释放内存
void FreeSpace_CircleLinkList(CircleLinkList* clist);
#endif
//CircleLinkList.c
#include"CircleLinkList.h"
//初始化函数
CircleLinkList* Init_CircleLinkList(){
CircleLinkList* clist = (CircleLinkList*)malloc(sizeof(CircleLinkList));
clist->head.next = &(clist->head);
clist->size = 0;
return clist;
}
//插入函数
void Insert_CircleLinkList(CircleLinkList* clist, int pos, CircleLinkNode* data){
if (clist == NULL){
return;
}
if (data == NULL){
return;
}
if (pos <0 || pos > clist->size){
pos = clist->size;
}
//根据位置查找结点
//辅助指针变量
CircleLinkNode* pCurrent = &(clist->head);
for (int i = 0; i < pos;i++){
pCurrent = pCurrent->next;
}
//新数据入链表
data->next = pCurrent->next;
pCurrent->next = data;
clist->size ++;
}
//获得第一个元素
CircleLinkNode* Front_CircleLinkList(CircleLinkList* clist){
return clist->head.next;
}
//根据位置删除
void RemoveByPos_CircleLinkList(CircleLinkList* clist, int pos){
if (clist == NULL){
return;
}
if (pos < 0 || pos >= clist->size){
return;
}
//根据pos找结点
//辅助指针变量
CircleLinkNode* pCurrent = &(clist->head);
for (int i = 0; i < pos;i++){
pCurrent = pCurrent->next;
}
//pCurrent->next = pCurrent->next->next;
//缓存当前结点的下一个结点
CircleLinkNode* pNext = pCurrent->next;
pCurrent->next = pNext->next;
clist->size--;
}
//根据值去删除
void RemoveByValue_CircleLinkList(CircleLinkList* clist, CircleLinkNode* data, COMPARENODE compare){
if (clist == NULL){
return;
}
if (data == NULL){
return;
}
#if 0
int pos = Find_CircleLinkList(clist, (CircleLinkNode*)&data, compare);
RemoveByPos_CircleLinkList(clist, pos);
#endif
//这个是循环链表
CircleLinkNode* pPrev = &(clist->head);
CircleLinkNode* pCurrent = pPrev->next;
int i = 0;
for (i = 0; i < clist->size; i++){
if (compare(pCurrent, data) == CIRCLELINKLIST_TRUE){
pPrev->next = pCurrent->next;
clist->size--;
break;
}
pPrev = pCurrent;
pCurrent = pPrev->next;
}
}
//获得链表的长度
int Size_CircleLinkList(CircleLinkList* clist){
return clist->size;
}
//判断是否为空
int IsEmpty_CircleLinkList(CircleLinkList* clist){
if (clist->size == 0){
return CIRCLELINKLIST_TRUE;
}
return CIRCLELINKLIST_FALSE;
}
//查找
int Find_CircleLinkList(CircleLinkList* clist, CircleLinkNode* data, COMPARENODE compare){
if (clist == NULL){
return -1;
}
if (data == NULL){
return -1;
}
CircleLinkNode* pCurrent = clist->head.next;
int flag = -1;
for (int i = 0; i < clist->size; i ++){
if (compare(pCurrent,data) == CIRCLELINKLIST_TRUE){
flag = i;
break;
}
pCurrent = pCurrent->next;
}
return flag;
}
//打印节点
void Print_CircleLinkList(CircleLinkList* clist, PRINTNODE print){
if (clist == NULL){
return;
}
//辅助指针变量
CircleLinkNode* pCurrent = clist->head.next;
for (int i = 0; i < (clist->size)*2;i++){ //*2是遍历次数
if (pCurrent == &(clist->head)){ //如果指向头节点,指向头节点的下一个
pCurrent = pCurrent->next;
printf("------------------\n");
}
print(pCurrent);
pCurrent = pCurrent->next;
}
}
//释放内存
void FreeSpace_CircleLinkList(CircleLinkList* clist){
if (clist == NULL){
return;
}
free(clist);
}
//循环链表main.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include"CircleLinkList.h"
typedef struct PERSON{
CircleLinkNode node;
char name[64];
int age;
int score;
}Person;
void MyPrint(CircleLinkNode* data){
Person* p = (Person*)data;
printf("Name:%s Age:%d Score:%d\n",p->name,p->age,p->score);
}
int MyCompare(CircleLinkNode* data1, CircleLinkNode* data2){
Person* p1 = (Person*)data1; //改变指针作用域
Person* p2 = (Person*)data2;
if (strcmp(p1->name,p2->name) ==0 && p1->age == p2->age&& p1->score == p2->score){
return CIRCLELINKLIST_TRUE;
}
return CIRCLELINKLIST_FALSE;
}
int main(void){
//创建循环链表
CircleLinkList* clist = Init_CircleLinkList();
//创建数据
Person p1, p2, p3, p4, p5;
strcpy(p1.name, "aaa");
strcpy(p2.name, "bbb");
strcpy(p3.name, "ccc");
strcpy(p4.name, "ddd");
strcpy(p5.name, "eee");
p1.age = 10;
p2.age = 20;
p3.age = 30;
p4.age = 40;
p5.age = 50;
p1.score = 50;
p2.score = 50;
p3.score = 60;
p4.score = 65;
p5.score = 70;
//数据入链表
Insert_CircleLinkList(clist, 100, (CircleLinkNode*)&p1);
Insert_CircleLinkList(clist, 100, (CircleLinkNode*)&p2);
Insert_CircleLinkList(clist, 100, (CircleLinkNode*)&p3);
Insert_CircleLinkList(clist, 100, (CircleLinkNode*)&p4);
Insert_CircleLinkList(clist, 100, (CircleLinkNode*)&p5);
//打印
Print_CircleLinkList(clist, MyPrint);
Person pDel;
strcpy(pDel.name,"ddd");
pDel.age = 40;
pDel.score = 65;
//根据值删除
RemoveByValue_CircleLinkList(clist, (CircleLinkNode*)&pDel, MyCompare);
//打印
printf("--------------\n");
Print_CircleLinkList(clist, MyPrint);
//释放内存
FreeSpace_CircleLinkList(clist);
system("pause");
return 0;
}
循环链表应用(约瑟夫问题)
n 个人围成一个圆圈,首先第 1 个人从 1 开始一个人一个人顺时针报数,报到第 m 个人,令其出列。然后再从下一 个人开始从 1 顺时针报数,报到第 m 个人,再令其出列,…,如此下去,求出列顺序。
可用循环链表的CircleLinkList.h与CircleLinkList.c文件
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "CircleLinkList.h"
#define M 8
#define N 3
typedef struct MYNUM{
CircleLinkNode node;
int val;
}MyNum;
void MyPrint(CircleLinkNode* data){
MyNum* num = (MyNum*)data;
printf("%d ",num->val);
}
int MyCompare(CircleLinkNode* data1, CircleLinkNode* data2){
MyNum* num1 = (MyNum*)data1; //改变指针作用域
MyNum* num2 = (MyNum*)data2;
if (num1->val == num2->val){
return CIRCLELINKLIST_TRUE;
}
return CIRCLELINKLIST_FALSE;
}
int main(void){
//创建循环链表
CircleLinkList* clist = Init_CircleLinkList();
//链表插入数据
MyNum num[M];
for (int i = 0; i < 8;i ++){
num[i].val = i + 1;
Insert_CircleLinkList(clist, i, (CircleLinkNode*)&num[i]);
}
//打印
Print_CircleLinkList(clist, MyPrint);
printf("\n");
int index = 1;
//辅助指针
CircleLinkNode* pCurrent = clist->head.next;
while (Size_CircleLinkList(clist) > 1){
if (index == N){
MyNum* temNum = (MyNum*)pCurrent;
printf("%d ", temNum->val);
//缓存待删除结点的下一个结点
CircleLinkNode* pNext = pCurrent->next;
//根据值删除
RemoveByValue_CircleLinkList(clist, pCurrent, MyCompare);
pCurrent = pNext;
if (pCurrent == &(clist->head)){
pCurrent = pCurrent->next;
}
index = 1;
}
pCurrent = pCurrent->next;
if (pCurrent == &(clist->head)){
pCurrent = pCurrent->next;
}
index++;
}
if (Size_CircleLinkList(clist) == 1){
MyNum* tempNum = (MyNum*)Front_CircleLinkList(clist);
printf("%d ",tempNum->val);
}
else{
printf("出错!");
}
printf("\n");
//释放链表内存
FreeSpace_CircleLinkList(clist);
system("pause");
return 0;
}
双向链表
为什么需要双向链表?
单链表的结点都只有一个指向下一个结点的指针
单链表的数据元素无法直接访问其前驱元素
逆序访问单链表中的元素是极其耗时的操作!
双向链表的定义
在单链表的结点中增加一个指向其前驱的pre指针
优点:
双向链表在单链表的基础上增加了指向前驱的指针
功能上双向链表可以完全取代单链表的使用
双向链表的Next,Pre和Current操作可以高效的遍历链表中的所有元素
缺点:
代码复杂
在普通位置插入节点
current->next = node;
node->next = next;
next->pre = node;
node->pre = current;
在空链表中插入节点
删除操作
current->next = next;
next->pre = current;
删除操作异常处理:当双向链表仅有一个节点的时候
如果辅助指针变量 pNext == NULL,那么 pNext 是没有前驱操作的。空指针既没有前驱也没有后继。
//DoubleLinkList.h
#ifndef DOUBLELINKLIST_H
#define DOUBLELINKLIST_H
#define DOUBLE_FALSE 0
#define DOUBLE_TRUE 1
#include<stdio.h>
#include<stdlib.h>
//链表结点
typedef struct DOUBLELISTNODE {
void* data;
struct DOUBLELISTNODE* next;
struct DOUBLELISTNODE* prev;
}DoublieListNode;
//链表结构体
typedef struct DOUBLELINKLIST {
DoublieListNode* head; //首结点
DoublieListNode* rear; //尾结点
int size; //链表长度
}DoubleLinkList;
//比较回调
typedef int(*COMPARENODE)(DoublieListNode*, DoublieListNode*);
//打印回调函数
typedef void(*PRINTNODE)(DoublieListNode*);
//初始化
DoubleLinkList* Init_DoubleLinkList();
//插入
void Insert_DoubleLinkList(DoubleLinkList* dlist, int pos, void* data);
//获得第一个元素
DoublieListNode* Front_DoubleLinkList(DoubleLinkList* dlist);
//根据位置删除
void RemoveByPos_DoubleLinkList(DoubleLinkList* dlist, int pos);
//根据值去删除
void RemoveByValue_DoubleLinkList(DoubleLinkList* dlist, DoublieListNode* data, COMPARENODE compare);
//获得链表的长度
int Size_DoublieLinkList(DoubleLinkList* dlist);
//判断是否为空
int IsEmpty_DoubleLinkList(DoubleLinkList* dlist);
//查找
int Find_DoubleLinkList(DoubleLinkList* clist, DoublieListNode* data, COMPARENODE compare);
//打印
void Print_DoubleLinkList(DoubleLinkList* clist, PRINTNODE print, int IsReverse);
//释放内存
void FreeSpace_DoubleLinkList(DoubleLinkList* clist);
#endif
//DoubleLinkList.c
#include "DoubleLinkList.h"
//初始化
DoubleLinkList* Init_DoubleLinkList() {
DoubleLinkList* dlist = (DoubleLinkList*)malloc(sizeof(DoubleLinkList));
if (dlist == NULL) {
return NULL;
}
dlist->head = NULL;
dlist->rear = NULL;
dlist->size = 0;
return dlist;
}
//插入
void Insert_DoubleLinkList(DoubleLinkList* dlist, int pos, void* data) {
//创建新结点
DoublieListNode* newnode = (DoublieListNode*)malloc(sizeof(DoublieListNode));
newnode->data = data;
newnode->next = NULL;
newnode->prev = NULL;
//第一次插入
if (dlist->head == NULL && dlist->rear == NULL) {
dlist->head = newnode;
dlist->rear = newnode;
//初始化head结点前驱和后继
dlist->head->next = NULL;
dlist->head->prev = NULL;
//初始化rear结点前驱和后继
dlist->rear->next = NULL;
dlist->rear->prev = NULL;
dlist->size++;
return ;
}
//头插法
if (pos == 0) {
newnode->prev = NULL;
newnode->next = dlist->head;
dlist->head->prev = newnode;
dlist->head = newnode;
dlist->size++;
return ;
}
//尾插法
if (pos >= dlist->size) {
dlist->rear->next = newnode;
newnode->prev = dlist->rear;
newnode->next = NULL;
dlist->rear = newnode;
dlist->size++;
return ;
}
//其他插入
DoublieListNode* pPrev = dlist->head;
DoublieListNode* pCurrent = pPrev->next;
for (int i = 1; i < pos; i++) {
pPrev = pCurrent;
pCurrent = pPrev->next;
}
newnode->next = pCurrent;
pCurrent->prev = newnode;
pPrev->next = newnode;
newnode->prev = pPrev;
dlist->size++;
return ;
}
//获得第一个元素
DoublieListNode* Front_DoubleLinkList(DoubleLinkList* dlist) {
return dlist->head->next;
}
//根据位置删除
void RemoveByPos_DoubleLinkList(DoubleLinkList* dlist, int pos) {
if (dlist == NULL) {
return;
}
if (pos < 0 || pos >= dlist->size) {
return;
}
DoublieListNode* pCurrent = dlist->head;
for (int i = 0; i < pos; i++) {
pCurrent = pCurrent->next;
}
DoublieListNode* pNext = pCurrent->next;
pCurrent->next = pNext->next;
pNext->prev = pCurrent;
dlist->size--;
}
//根据值去删除
void RemoveByValue_DoubleLinkList(DoubleLinkList* dlist, DoublieListNode* data, COMPARENODE compare) {
if (dlist == NULL) {
return;
}
if (data == NULL) {
return;
}
}
//获得链表的长度
int Size_DoublieLinkList(DoubleLinkList* dlist) {
return dlist->size;
}
//判断是否为空
int IsEmpty_DoubleLinkList(DoubleLinkList* dlist) {
if (dlist->size == 0) {
return DOUBLE_TRUE;
}
return DOUBLE_FALSE;
}
//查找
int Find_DoubleLinkList(DoubleLinkList* dlist, DoublieListNode* data, COMPARENODE compare) {
if (dlist == NULL) {
return -1;
}
if (data == NULL) {
return -1;
}
DoublieListNode* pCurrent = dlist->head->next;
int flag = -1;
for (int i = 0; i < dlist->size; i++) {
if (compare(pCurrent, data) == DOUBLE_TRUE) {
flag = i;
break;
}
pCurrent = pCurrent->next;
}
return flag;
}
//打印
void Print_DoubleLinkList(DoubleLinkList* dlist, PRINTNODE print,int IsReverse) {
DoublieListNode* pCurrent = NULL;
//true 表示 逆序遍历
if (IsReverse == DOUBLE_TRUE) {
pCurrent = dlist->rear;
while (pCurrent != NULL) {
print(pCurrent);
pCurrent = pCurrent->prev;
}
}
else {
pCurrent = dlist->head;
while (pCurrent != NULL) {
print(pCurrent);
pCurrent = pCurrent->next;
}
}
}
//释放内存
void FreeSpace_DoubleLinkList(DoubleLinkList* dlist) {
if (dlist == NULL) {
return;
}
free(dlist);
}
//双向链表main.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "DoubleLinkList.h"
typedef struct TEACHER {
char name[64];
int age;
}Teacher;
void PrintNode(DoublieListNode* node) {
Teacher* teacher = (Teacher*)(node->data);
printf("Name:%s,Age:%d\n", teacher->name, teacher->age);
}
void test01() {
//创建双向链表
DoubleLinkList* dlist = Init_DoubleLinkList();
//创建数据
Teacher t1, t2, t3;
t1.age = 10;
t2.age = 20;
t3.age = 30;
strcpy(t1.name, "aaa");
strcpy(t2.name, "bbb");
strcpy(t3.name, "ccc");
//插入数据
Insert_DoubleLinkList(dlist, 0, &t1);
Insert_DoubleLinkList(dlist, 0, &t2);
Insert_DoubleLinkList(dlist, 0, &t3);
//打印
printf("------逆序遍历-----------\n");
Print_DoubleLinkList(dlist,PrintNode, DOUBLE_TRUE);
printf("-------正向遍历----------\n");
Print_DoubleLinkList(dlist, PrintNode, DOUBLE_FALSE);
}
int main(void) {
test01();
system("pause");
return 0;
}