链表的概念:物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链实现。
今天,我们就谈谈单向非循环链表(包括带头结点和不带头结点)
1.单向非循环链表结构定义以及初始化
//结构定义
typedef int Datatype;
typedef struct ListNode {
Datatype Data; //数据域
struct ListNode *Next; //指针域
}ListNode,*List;
//初始化
//1.不带头结点
void ListInit(List *plist){
*plist= NULL;
}
//2.带头结点
void ListInit(List *plist) {
*plist = (ListNode *)malloc(sizeof(ListNode));
(*plist)->Next = NULL;
}
下面我们主要对带头结点的链表进行操作(不带头节点的链表在操作过程中需要对第一个结点进行单独处理,大家可以自行敲击)
2.插入结点
//尾插法
void ListCreate_Tail(List *phead, Datatype x){
assert(phead != NULL);
ListNode *p =*phead;
ListNode *s = (ListNode *)malloc(sizeof(ListNode));
assert(s != NULL);
while (p->Next!= NULL) {
p = p->Next;
}
s->Data = x;
s->Next = NULL;
p->Next = s;
}
//头插法
void ListCreate_Head(List *phead, Datatype x) {
assert(phead != NULL);
ListNode *p = *phead;
ListNode *s = (ListNode *)malloc(sizeof(ListNode));
assert(s != NULL);
s->Data = x;
s->Next = NULL;
s->Next = (*phead)->Next;
(*phead)->Next = s;
}
//按值插入(前提链表为顺序链表)
void ListInsertVal(List *phead, Datatype val) {
assert(phead != NULL);
ListNode *p = (*phead)->Next;
ListNode *q = p->Next;
while (q != NULL) {
if (val < (*phead)->Next->Data) {
ListCreate_Head(phead, val); //插入元素小于链表所有元素头插法
}
else if (val > p->Data&&val<q->Data) {
ListNode *s = (ListNode *)malloc(sizeof(ListNode)); //中间插入
s->Data = val;
s->Next = p->Next;
p->Next = s;
}
p = p->Next;
q = q->Next;
}
ListCreate_Tail(phead, val); //插入元素大于链表所有元素,采用尾插法
}
3.删除结点
//尾删法
void ListPopBack(List *phead) {
assert(phead != NULL);
ListNode *p = *phead;
if (p->Next == NULL) {
printf("该链表无元素。无法删除元素!\n");
return;
}
while (p->Next->Next!= NULL) {
p = p->Next;
}
ListNode *s = (ListNode *)malloc(sizeof(ListNode));
assert(s != NULL);
s = p->Next;
p->Next = NULL;
free(s);
}
//头删法
void ListPopFront(List *phead) {
assert(phead != NULL);
ListNode *p = *phead;
if (p->Next == NULL) {
printf("该链表无元素。无法删除元素!\n");
return;
}
p = p->Next;
(*phead)->Next = p->Next;
free(p);
}
//按值删除
void ListErasebyval(List *phead, Datatype val) {
assert(phead != NULL);
ListNode *p =*phead;
ListNode *pre = NULL;
while (p->Next!= NULL) {
pre = p;
p = p->Next;
if (val == p->Data) {
pre->Next = p->Next;
free(p);
return;
}
}
printf("无该元素,无法删除!\n");
return;
}
4查找
//按值查找(返回结点地址)
ListNode *ListFind(ListNode *phead, Datatype val) {
assert(phead != NULL);
ListNode *p = phead->Next;
while (p != NULL) {
if (p->Data == val) {
return p;
}
p = p->Next;
}
return NULL;
}
5.排序
//排序
void ListSort(List *phead) {
assert(phead != NULL);
ListNode *tmp = (*phead)->Next, *prev = NULL;
ListNode *p = (*phead)->Next;
ListNode *q = p->Next;
p->Next = NULL;
while (q != NULL)
{
p = q;
q = q->Next; //摘取第二个结点
//寻找插入的位置
while (tmp != NULL && p->Data > tmp->Data) //元素比该结点元素值大
{
prev = tmp; //保留前驱结点,方便插入
tmp = tmp->Next; //向后移动
}
if (prev == NULL) //说明元素值比链表所有元素值都小
{
p->Next = (*phead)->Next; //头插
(*phead)->Next = p;
}
else //
{
p->Next = prev->Next;
prev->Next = p;
}
tmp = (*phead)->Next; //插入后更新tmp与prev指针
prev = NULL;
}
}
6.求表头和表尾元素
//求表头元素
Datatype ListFront(ListNode *phead) {
assert(phead != NULL);
ListNode *p = phead->Next;
if (p != NULL) {
return p->Data;
}
printf("该链表无元素!\n");
return -1;
}
//求表尾元素
Datatype ListBack(ListNode *phead) {
assert(phead != NULL);
ListNode *p = phead->Next;
if (p == NULL) {
printf("该链表无元素!\n\n");
return -1;
}
while (p->Next!= NULL) {
p = p->Next;
}
return p->Data;
}
7.清除所有结点以及摧毁元素
void ListClear(List *phead) {
assert(phead != NULL);
while ((*phead)->Next != NULL) {
ListPopFront(phead); //进行头删法,直到头结点的前驱无元素.
}
}
//摧毁链表
void ListDestroy(List *phead) {
assert(phead != NULL);
ListNode *p = (*phead);
ListClear(phead);
free(p);
}
8.转置
//转置
void ListReverse(List *phead) {
assert(phead != NULL);
ListNode *p = (*phead)->Next;
ListNode *q= p->Next;
p->Next = NULL;
while (q != NULL) {
p = q;
q = q->Next;
p->Next = (*phead)->Next;
(*phead)->Next = p;
}
}
9.遍历
void ShowList(ListNode *phead) {
assert(phead != NULL);
ListNode *p =phead->Next;
while (p != NULL) {
printf("%d->", p->Data);
p = p->Next;
}
printf("over.\n");
}
上边是关于带头结点的单向非循环链表的一些相关操作,当然链表的结构多种多样,大家有兴趣可以深入了解数据结构。