数据结构 — 链表
文章目录
前言
此部分为单链表,未做图例说明讲解,仅为代码理解部分。后续有时间补上详尽
一、定义链表
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
// #include <stdbool.h>
typedef int ElemType; // 自定义链表的数据元素为整数。
typedef enum {
false = 0,
true
}bool;
/*定义链表*/
typedef struct LNode{
ElemType data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList;
//LNode 等价于 struct LNode (结点)
//LinkList 等价于 struct LNode * (链表)
二、创建链表:createList_pHead
/*创建链表*/
LinkList createList_pHead(void)
{
int len = 0; //用来存放有效结点的个数
int i = 0;
int val = 0; //用来临时存放用户输入结点的值
//分配一个不存放有效数据的头结点
LinkList pHead = (LinkList)malloc(sizeof(LNode));
if(pHead == NULL) {
printf("分配失败,终止程序!\n");
exit(-1);
// return NULL;
}
/*定义pTail始终指向尾结点,开始时指向头结点*/
LinkList pTail = pHead;
pTail->next = NULL;
printf("输入需要生成的链表结点个数:len = ");
scanf("%d",&len);
for(i = 0; i < len ; i++)
{
printf("输入第%d个结点的值:",i+1);
scanf("%d",&val);
/*申请新结点*/
LinkList pNew = (LinkList)malloc(sizeof(LNode));
if(NULL == pNew){
printf("分配失败,终止程序!\n");
exit(-1);
}
pNew->data = val; /*将值赋值给新结点的数据域*/
pTail->next = pNew; /*pNew(地址)赋值给pTail的指针域,尾结点指向新结点*/
pNew->next = NULL; /*pNew清空*/
pTail = pNew; /*pNew赋值给pTail,更新尾部指针。(pNew作为链表的尾指针,为下次指向新结点做准备)*/
}
return pHead;
}
三、遍历链表:traverseList_OUT
/*遍历链表输出*/
bool traverseList_OUT(LinkList pHead)
{
LinkList p = pHead->next; /*p开始指向第一个有效结点 (pHead->next)*/
/*当p不为空,就一直输出数值*/
while (NULL != p)
{
printf("%d ",p->data);
p = p->next;/*指向下一个结点,相当于p++,但是链表不是连续的,所以不能使用地址++*/
}
printf("\n");
return true;
}
四、链表判空:isEmpty
/*判断链表是否为空,返回值:false-非空或失败,true-空。*/
bool isEmpty(LinkList pHead)
{
if(NULL == pHead->next)
return true;
else
return false;
}
不为空测试
为空测试
五、获取链表长度:getLengthList
/* 求链表的长度,返回值:>=0-表pHead结点的个数。*/
int getLengthList(LinkList pHead)
{
LNode *pp=pHead->next; // 头结点不算,从第1个结点开始。
int length=0;
while (pp != NULL)
{
pp=pp->next;
length++;
}
return length;
}
测试:
六、链表排序:sortList
/*链表排序*/
void sortList(LinkList pHead)
{
#if 0
/*
//数组类型排序,作为参考
int i,j,t;
for(i = 0 ; i < len-1; i++)
for(j = i+1; j < len; j++)
{
if(a[i] > a[j])
{
t = a[i];
a[i] = a[j];
a[j] = t;
}
}
*/
int i,j,t;
int len = getLengthList(pHead);
LinkList p,q;
for(i = 0,p = pHead->next; i < len - 1; i++,p = p->next)
{
for(j = i+1,q = p->next; j < len; j++,q = q->next)
{
if(p->data > q->data) //类似于数组中的: a[i] > a[j]
{
t = p->data; //类似数组中的:t = a[i]
p->data = q->data; //类似于数组中的: a[i] = a[j]
q->data = t; //类似于数组中的: a[j] = t
}
}
}
#else
/*
//冒泡排序
int i,j,t;
int len = getLengthList(pHead);
for(i = 0; i < len-1; i++)
for(j = 0; j < len-1-i; j++)
{
if(a[j] > a[j+1])
{
t = a[j];
a[j] = a[j+1];
a[j+1] = t;
}
}
*/
int i,j,t;
int len = getLengthList(pHead);
LinkList p,q;
for(i = 0,p = pHead->next; i < len-1; i++,p = p->next)
for(j = 0,q = pHead->next; j < len-1-i; j++,q = q->next)
{
if(q->data > q->next->data)
{
t = q->data;
q->data = q->next->data;
q->next->data = t;
}
}
#endif
}
测试:
七、链表插入:insertList
/*链表插入 */
//在链表指定位置插入给定的值
//在链表的第 i 个位置之前插入元素 e
int insertList(LinkList pHead, int pos, int val)
{
if(pos < 1) return -1; //不合法
int i = 0;
LNode *p = pHead; //pHead为头指针,p为头结点
/*寻找第i个结点*/
while( p!=NULL && i < pos-1)
{
p = p->next;
++i;
}
/*第i个结点不存在*/
if(i > pos-1 || NULL == p)
return false;
/*生成新结点*/
LNode *pNew = (LNode *)malloc(sizeof(LNode));
if(pNew == NULL)
{
printf("动态分配内存失败!\n");
exit(-1);
}
pNew->data = val;
pNew->next = p->next; /*将p的下一个结点地址赋值给pNew的指针域,即pNew->next指向p->next*/
p->next = pNew; /*将pNew地址赋值给p的指针域,即p->next指向pNew*/
return true;
}
八、删除链表元素:deleteList
/*链表删除 */
//在链表指定位置删除给定的值
//在链表的第 i 个位置之前插入元素 e
int deleteList(LinkList pHead, int pos, int *pVal)
{
if(pos < 1) return -1; //不合法
int i = 0;
LNode *p = pHead; //pHead为头指针,p为头结点
/*寻找第i个结点*/
while( p->next!=NULL && i < pos-1)
{
p = p->next;
++i;
}
/*第i个结点不存在*/
if(i > pos-1 || NULL == p->next)
return false;
LinkList q = p->next; //不加这个,容易造成内存泄漏
*pVal = q->data;
p->next = p->next->next;
free(q);
return true;
}
九、清空链表:ClearList
// 清空链表。
void ClearList(LinkList pHead)
{
// 清空链表LL是指释放链表全部的结点,但不包括头结点。
if (pHead == NULL) { printf("链表LL不存在。\n"); return; } // 判断链表是否存在。
LNode *tmp1;
LNode *tmp2=pHead->next; // 保留头结点,从头结点的下一个结点开始释放。
while(tmp2!=NULL)
{
tmp1=tmp2->next;
free(tmp2);
tmp2=tmp1;
}
pHead->next=NULL; // 这行代码一定不能少,否则会留下野指针。
return;
}
测试:
十、链表元素反转:reverseList
// 把链表pp结点之后的结点原地逆置(反转),返回值:0-失败;1-成功。
void reverseList(LNode *pp)
{
LNode *ss; // 当前结点。
LNode *ssnext; // 当前结点的下一结点。
ss=pp->next; // 从pp结点之后的结点开始反转。
pp->next=NULL; // pp->next指向空。
while (ss != NULL)
{
ssnext=ss->next; // 保留ss下一结点的地址。
// 以下两行相当于在pp之后插入ss结点。
ss->next=pp->next;
pp->next=ss;
ss=ssnext; // ss结点后移。
}
}
// 采用归并的方法,将两个升序的链表La和Lb,合并成一个升序的链表Lc。
int mergeList(LinkList La,LinkList Lb,LinkList Lc)
{
if ( (La == NULL) || (Lb == NULL) || (Lc == NULL) ) { printf("表La、Lb、Lc至少有一个不存在。\n"); return 0; }
La=La->next;
Lb=Lb->next;
LNode *pp;
// 把La和Lb合并到Lc中。
while ( (La != NULL) && (Lb != NULL) )
{
// 取La和Lb的较小者。
if (La->data <= Lb->data)
{
pp=La; La=La->next;
}
else
{
pp=Lb; Lb=Lb->next;
}
// 把较小者追加到Lc中。
Lc->next=(LNode *)malloc(sizeof(LNode)); // 分配一个新结点。
Lc=Lc->next;
memcpy(&Lc->data,&pp->data,sizeof(ElemType));
Lc->next=NULL;
}
// 把链表La其它的元素追加到Lc中。
while (La != NULL)
{
Lc->next=(LNode *)malloc(sizeof(LNode)); // 分配一个新结点。
Lc=Lc->next;
memcpy(&Lc->data,&La->data,sizeof(ElemType));
Lc->next=NULL;
La=La->next;
}
// 把链表Lb其它的元素追加到Lc中。
while (Lb != NULL)
{
Lc->next=(LNode *)malloc(sizeof(LNode)); // 分配一个新结点。
Lc=Lc->next;
memcpy(&Lc->data,&Lb->data,sizeof(ElemType));
Lc->next=NULL;
Lb=Lb->next;
}
return 1;
}
十一、实验
int main(void)
{
int val;
LinkList pHead = NULL;
pHead = createList_pHead();
printf("生成的链表数值为:\n");
traverseList_OUT(pHead);
/*插入*/
printf("在第3个位置插入 22\n");
if(insertList(pHead,3,22) == 1){
traverseList_OUT(pHead);
printf("插入成功\n");
}else{
printf("插入失败\n");
}
/*删除*/
printf("删除第4个元素\n");
if(deleteList(pHead,4,&val)){
printf("删除成功,你删除的元素是:%d\n",val);
}else{
printf("删除失败,你删除的元素是:%d\n",val);
}
traverseList_OUT(pHead);
/*排序*/
printf("生成的链表升序之后为:\n");
sortList(pHead);
traverseList_OUT(pHead);
/*链表逆转*/
printf("生成的链表反转之后为:\n");
reverseList(pHead);
traverseList_OUT(pHead);
/*清空链表*/
printf("清空链表!\n");
ClearList(pHead);
traverseList_OUT(pHead);
/*判空*/
if(isEmpty(pHead)) printf("链表为空!\n");
else printf("链表不为空\n");
/*获取长度*/
int len = getLengthList(pHead);
printf("链表的长度为:%d",len);
return 0;
}
总结
待续
2021/10/08
双链表,循环链表……