概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。线性表可以采用链式结构进行存储,链式存储结构主要有单链表和双链表。线性表的顺序存储空间是整体分配的,逻辑上相邻的两个元素,物理上也相邻。因此存取表中任一元素十分简单,但插入和删除运算都需要移动大量的元素。而线性表的链式结构中每个元素的存储结构作为一个结点单独分配,因此逻辑上相邻的元素对应的结点在物理不一定是相邻的,通过增加指针域表示逻辑关系,在插入和删除操作时只需要修改相应的指针域,从而解决了顺序表中插入和删除操作需要移动大量元素的弱点,但同时也失去了可随机存取的优点。线性表链式存储结定义:在链式结构中,把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。每个结点只包含一个指针域的链表,叫做单链表。单链表正是通过每个结点的指针域将线性表的数据元素按其逻辑次序链接在一起。
注意:
1.从上图可看出,链式结构在逻辑上是连续的,但是在物理上不一定连续。
2. 现实中的结点一般都是从堆上申请出来的。
3.从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续。
在实际存内存中结点与结点之间是没有箭头连接的,箭头是我们为了更容易理解而想象出来的,在真实的物理结构中,是结点的指针域存储着下一个结点的地址实现次序链接。
为了方便理解我们采用逻辑结构进行讲解。
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;//数值域
struct SListNode* next;//指针域
}SLTNode;
2初始化线性表:
SLTNode* BuySLTNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//在堆区申请内存空间
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
SLTNode* CreateSList(int n)
{
SLTNode* phead = NULL, *ptail = NULL;
int x = 0;
for (int i = 0; i < n; ++i)
{
SLTNode* newnode = BuySLTNode(i + 10);
if (phead == NULL)
{
ptail = phead = newnode;
}
else
{
ptail->next = newnode;
ptail = newnode;
}
}
执行以上程序数据在内存的存储逻辑是这样的
现在我们想在14后面的加入一个新的结点,我们只需要找到14这个结点,并将这个结点的指针域指向新结点的地址即可;
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuySLTNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* tail = *pphead;
// 找到最后一个结点的位置
while (tail->next)
{
tail = tail->next;
}
tail->next = newnode;//改变指针域的指向
}
}
总结:在进行尾插数据时原有链表为空时我们需要改变头指针的值,这时需要传头指针的地址(形参是实参的临时拷贝,形参的改变不会影响实参);
4头插结点:
在创建好的链表中,在前面增加一个新的结点,使这个结点成为链表的第一个结点。
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode= BuySLTNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
5尾删结点:
在创建好的链表中删除最后一个节点。
void SLTPopBack(SLTNode** pphead)
{
assert(*pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* ptail = *pphead;
while (ptail->next->next)//找到倒数第二给结点的位置
{
ptail = ptail->next;
}
free(ptail->next );
ptail->next = NULL;
}
}
6头删结点:
void SLTPopFront(SLTNode** pphead)
{
assert(*pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* ptail = *pphead;
*pphead = ptail->next;
free(ptail);
ptail = NULL;
}
}
7删除某个结点:在创建好的链表中,找到某个值所在的节点,并将这个节点删除。
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
SLTNode* ptail = phead;
while (ptail)
{
if (ptail->data == x)
{
printf("找到了\n");
return ptail;
}
ptail=ptail->next;
}
printf("没有\n");
return NULL;
}
void SLTErase(SLTNode** pphead, SLTNode*pos)
{
assert(pos);
assert(*pphead);
if (*pphead==pos)
{
SLTNode* tmp = *pphead;
*pphead = (*pphead)->next;
free(tmp);
tmp = NULL;
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
//pos = NULL;
}
}
8在pos之前插入X:
// 在pos之前插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
if (*pphead == pos)
{
SLTPushFront(pphead, x);
}
else if (pos == NULL)
{
SLTPushFront(pphead, x);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SLTNode* newnode = BuySLTNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
9按值删除:将链表中结点的数据域打数据元素为某值的节点删除。
实现如图功能
SLTNode* removeElement(SLTNode* phead, SLTDataType data)
{
assert(phead);
if (phead==NULL)
return NULL;
SLTNode* cur = phead;
SLTNode* newnode;
SLTNode* ptail;
newnode = ptail = NULL;
while (cur)
{
if (cur->data != data)
{
if (ptail == NULL)
{
newnode = ptail = cur;
}
else
{
ptail->next = cur;
ptail = ptail->next;
}
cur = cur->next;
}
else
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
}
if (ptail)
ptail->next = NULL;
return newnode;
}
Test.c
#include "SList.h"
//int main()
//{
// //int x = 6;
// //SLTNode* plist = CreateSList(x);
//
// //SLTPrint(plist);
//
// printf("[%d|%p]", plist->data, plist->next);
// ///*SLTPushBack(&plist, 10086);
//
// //SLTPrint(plist);
//
//
// //SLTPopBack(&plist);
// //SLTPrint(plist);
// //SLTPushFront(&plist, 10010);
// //SLTPrint(plist);*/
// //SLTNode* pos=SLTFind(plist, 11);
// //SLTDeletePos(&plist, pos);
//
// //SLTPrint(plist);
//
// //SLTDestory(&plist);
// //SLTPrint(plist);
//
//
// return 0;
//}
//
//
void menu()
{
printf("1、尾插数据 2、尾删数据\n");
printf("3、头插数据 4、头删数据\n");
printf("5、打印数据 -1、退出\n");
printf("6、删除X值 7、pos前插入\n");
printf("8、按值删除 \n");
}
int main()
{
SLTDataType x=0;
SLTNode* plist = CreateSList(x);
int option = 0;
SLTDataType val = 0;
do
{
menu();
printf("请输入你的操作:>");
scanf_s("%d", &option);
switch (option)
{
case 1:
printf("请依次输入你要尾插的数据,以-1结束\n");
scanf_s("%d", &val);
while (val != -1)
{
SLTPushBack(&plist, val);
scanf_s("%d", &val);
}
system("cls");
SLTPrint(plist);
break;
case 2:
system("cls");
SLTPopBack(&plist);
break;
case 3:
{
SLTDataType x = 0;
printf("请依次输入你要头插的数据,以-1结束\n");
scanf_s("%d", &x);
while (x != -1)
{
SLTPushFront(&plist, x);
scanf_s("%d", &x);
}
system("cls");
break;
}
case 4:
SLTPopFront(&plist);
system("cls");
break;
case 5:
system("cls");
SLTPrint(plist);
break;
default:
break;
case 6:
{
SLTDataType data = 0;
printf("输入你要删除的值\n");
scanf_s("%d", &data);
SLTNode*pos= SLTFind(plist, data);
SLTErase(&plist, pos);
system("cls");
SLTPrint(plist);
}
break;
case 7:
{
SLTDataType x;
printf("pos前插入\n");
scanf_s("%d", &x);
SLTNode* pos = SLTFind(plist, x);
SLTDataType n=0;
printf("请输入你要插入的值\n");
SLTInsert(&plist, pos, x);
system("cls");
SLTPrint(plist);
break;
}
case 8:
{
SLTDataType data = 0;
printf("请输入你要删除的值\n");
scanf_s("%d", &data);
SLTNode* plist1=removeElement(plist, data);
SLTPrint(plist1);
break;
}
}
} while (option != -1);
//SLDestroy(&s);
return 0;
}
SList.h
#pragma once
#include
#include
#include
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
SLTNode* BuySLTNode(SLTDataType x);
SLTNode* CreateSList(int n);
void SLTPrint(SLTNode* phead);
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopFront(SLTNode** pphead);
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
void SLTInsertAfert(SLTNode* pos, SLTDataType x);
//按值查找删除
void SLTPopVal(SLTNode** pphead, SLTDataType x);
//删除pos位置
void SLTDeletePos(SLTNode** pphead, SLTNode* pos);
void SLTDestory(SLTNode** pphead);
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
void SLTErase(SLTNode** pphead, SLTNode* pos);
SLTNode* removeElement(SLTNode** pphead, SLTDataType data);
SList.c
#include "SList.h"
SLTNode* BuySLTNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
SLTNode* CreateSList(int n)
{
SLTNode* phead = NULL, * ptail = NULL;
int x = 0;
for (int i = 0; i < n; ++i)
{
SLTNode* newnode = BuySLTNode(i + 10);
if (phead == NULL)
{
ptail = phead = newnode;
}
else
{
ptail->next = newnode;
ptail = newnode;
}
}
return phead;
}
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
while (cur != NULL)
{
printf("[%d|%p]->", cur->data, cur->next);
cur = cur->next;
}
printf("NULL\n");
}
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode = BuySLTNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
SLTNode* ptail = *pphead;
while (ptail->next)
{
ptail = ptail->next;
}
ptail->next = newnode;
}
}
void SLTPopBack(SLTNode** pphead)
{
assert(*pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* ptail = *pphead;
while (ptail->next->next)//找到倒数第二给结点的位置
{
ptail = ptail->next;
}
free(ptail->next );
ptail->next = NULL;
}
}
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
SLTNode* newnode= BuySLTNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
SLTNode* ptail = phead;
while (ptail)
{
if (ptail->data == x)
{
printf("找到了\n");
return ptail;
}
ptail=ptail->next;
}
printf("没有\n");
return NULL;
}
//在某个结点之后插入结点
void SLTInsertAfert(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode= BuySLTNode(x);
newnode->next = pos->next ;
pos->next = newnode;
}
void SLTErase(SLTNode** pphead, SLTNode*pos)
{
assert(*pphead);
if (*pphead==pos)
{
SLTNode* tmp = *pphead;
*pphead = (*pphead)->next;
free(tmp);
tmp = NULL;
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
//pos = NULL;
}
}
void SLTDeletePos(SLTNode** pphead, SLTNode* pos)
{
assert(pos);
assert(*pphead);
if ((*pphead)->next == NULL)
{
SLTPopBack(pphead);
}
else
{
SLTNode* ptail = *pphead;
while (ptail->next!=pos)
{
ptail= ptail->next;
}
ptail->next = pos->next;
//ptail->next = ptail->next->next;
free(pos);
pos = NULL;
}
}
void SLTDestory(SLTNode** pphead)
{
assert(*pphead);
while (*pphead)
{
SLTNode* ptail = *pphead;
*pphead=(*pphead)->next;
ptail->next = NULL;
free(ptail);
}
}
void SLTPopFront(SLTNode** pphead)
{
assert(*pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* ptail = *pphead;
*pphead = ptail->next;
free(ptail);
ptail = NULL;
}
}
// 在pos之前插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
if (*pphead == pos)
{
SLTPushFront(pphead, x);
}
else if (pos == NULL)
{
SLTPushFront(pphead, x);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SLTNode* newnode = BuySLTNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
SLTNode* removeElement(SLTNode* phead, SLTDataType data)
{
assert(phead);
if (phead==NULL)
return NULL;
SLTNode* cur = phead;
SLTNode* newnode;
SLTNode* ptail;
newnode = ptail = NULL;
while (cur)
{
if (cur->data != data)
{
if (ptail == NULL)
{
newnode = ptail = cur;
}
else
{
ptail->next = cur;
ptail = ptail->next;
}
cur = cur->next;
}
else
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
}
if (ptail)
ptail->next = NULL;
return newnode;
}