以“结点的序列”表示线性表称作线性链表(单链表)
单链表是链式存取的结构,为找第 i 个数据元素,必须先找到第 i-1 个数据元素。
因此,查找第 i 个数据元素的基本操作为:移动指针,比较 j 和 i
1、链接存储方法
链接方式存储的线性表简称为链表(Linked List)。
链表的具体存储表示为:
① 用一组任意的存储单元来存放线性表的结点(这组存储单元既可以是连续的,也可以是不连续的)
② 链表中结点的逻辑次序和物理次序不一定相同。为了能正确表示结点间的逻辑关系,在存储每个结点值的同时,还必须存储指示其后继结点的地址(或位置)信息(称为指针(pointer)或链(link))
注意:
链式存储是最常用的存储方式之一,它不仅可用来表示线性表,而且可用来表示各种非线性的数据结构。
2、链表的结点结构
┌───┬───┐
│data │next │
└───┴───┘
data域--存放结点值的数据域
next域--存放结点的直接后继的地址(位置)的指针域(链域)
注意:
①链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起的。
②每个结点只有一个链域的链表称为单链表(Single Linked List)。
3、头指针head和终端结点指针域的表示
单链表中每个结点的存储地址是存放在其前趋结点next域中,而开始结点无前趋,故应设头指针head指向开始结点。
注意:
链表由头指针唯一确定,单链表可以用头指针的名字来命名。
终端结点无后继,故终端结点的指针域为空,即NULL。
单链表中每个结点的存储地址是存放在其前趋结点next域中,而开始结点无前趋,故应设头指针head指向开始结点。
注意:
链表由头指针唯一确定,单链表可以用头指针的名字来命名。
终端结点无后继,故终端结点的指针域为空,即NULL。
4、单链表类型描述
typedef char DataType; //假设结点的数据域类型为字符
typedef struct node{ //结点类型定义
DataType data; //结点的数据域
struct node *next;//结点的指针域
}ListNode;
typedef ListNode *LinkList;
ListNode *p;
LinkList head;
注意:
①LinkList和ListNode是不同名字的同一个指针类型(命名的不同是为了概念上更明确)
②*LinkList类型的指针变量head表示它是单链表的头指针
③ListNode类型的指针变量p表示它是指向某一结点的指针
字*p访问结点分量
typedef char DataType; //假设结点的数据域类型为字符
typedef struct node{ //结点类型定义
DataType data; //结点的数据域
struct node *next;//结点的指针域
}ListNode;
typedef ListNode *LinkList;
ListNode *p;
LinkList head;
注意:
①LinkList和ListNode是不同名字的同一个指针类型(命名的不同是为了概念上更明确)
②*LinkList类型的指针变量head表示它是单链表的头指针
③ListNode类型的指针变量p表示它是指向某一结点的指针
字*p访问结点分量
链表操作中动态存储分配要使用标准函数,先介绍一下这些函数。
(1)malloc(size)
在内存的动态存储区申请一个长度为size字节的连续空间。
(2)calloc(n,size)
在内存的动态存储区申请n个长度为size字节的连续空间,函数返回值为分配空间的首地址。若此函数未被成功执行,函数返回值为0。
(3)free(p)
释放由指针p所指向的存储单元,而存储单元的大小是最近一次调用malloc()或calloc()函数时所申请的存储空间。
在头文件\"stdlib.h”中包含了这些函数的信息,使用这些函数时需在程序开头用文件包含指令#include“stdlib.h”指明。
另请读者注意,调用动态存储分配函数返回的指针是指向void类型或char类型的指针,在具体使用时,要根据所指向的数据进行强制类型转换。
单链表的建立有头插法、尾插法两种方法。
1.头插法
单链表是用户不断申请存储单元和改变链接关系而得到的一种特殊数据结构,将链表的左边称为链头,右边称为链尾。头插法建单链表是将链表右端看成固定的,链表不断向左延伸而得到的。头插法最先得到的是尾结点。
由于链表的长度是随机的,故用一个while循环来控制链表中结点个数。假设每个结点的值都大于O,则循环条件为输入的值大于o。申请存储空间可使用malloc()函数实现,需设立一申请单元指针,但malloc()函数得到的指针并不是指向结构体的指针,需使用强制类型转换,将其转换成结构体型指针。刚开始时,链表还没建立,是一空链表,head指针为NULL。
链表建立的过程是申请空间、得到数据、建立链接的循环处理过程。
2.尾插法
若将链表的左端固定,链表不断向右延伸,这种建立链表的方法称为尾插法。尾插法建立链表时,头指针固定不动,故必须设立一个搜索指针,向链表右边延伸,则整个算法中应设立三个链表指针,即头指针head、搜索指针p2、申请单元指针pl。尾插法最先得到的是头结点。
List.h
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
typedef int DataType;
typedef struct Node
{
DataType _data;
struct Node* _pNext;
}Node, *PNode;
void InitList(PNode* pHead);// 初始化单链表
void PushBack(PNode* pHead, DataType data);// 在单链表的尾部插入一个节点
void PopBack(PNode* pHead);
void PushFront(PNode* pHead, DataType data);// 在单链表的头部插入值为data的结点
void PopFront(PNode* pHead);// 删除单链表的第一个结点
Node* Find(PNode pHead, DataType data);
// 在单链表中查找值为data的结点,找到了返回该结点的地址,否则返回NULL
void Insert(PNode pos, DataType data);// 在单链表pos位置后插入值为data的结点
void Erase(PNode* pHead, PNode pos);
void Remove(PNode* pHead, DataType data);// 移除单链表中第一个值为data的结点
void RemoveAll(PNode* pHead, DataType data);// 移除单链表中所有值为data的结点
size_t Size(PNode pHead);// 获取单链表总结点的总个数
int Empty(PNode pHead);// 判断结点是否为空
PNode Back(PNode pHead);// 返回单链表的最后一个结点的位置
PNode Front(PNode pHead);// 返回单链表的第一个结点的位置
Node* BuyNode(DataType data);// 构建一个新节点
void PrintList(PNode pHead);// 正向打印单链表
void PrintFromTail2Head(PNode pHead);// 逆序打印单链表
void DeleteNotTailNode(PNode pos);// 删除单链表的非尾结点(不能遍历单链表)
List.c
#include "List.h"
void InitList(PNode* pHead)
{
assert(pHead);
*pHead = NULL;
}
Node* BuyNode(DataType data)
{
Node *pNewNode = NULL;
pNewNode = (Node *)malloc(sizeof(Node));
if(pNewNode != NULL)
{
pNewNode->_data = data;
pNewNode->_pNext = NULL;
}
return pNewNode;
}
void PushBack(PNode* pHead, DataType data)
{
assert(pHead);
if(*pHead == NULL)
{
*pHead = BuyNode(data);
}
else
{
Node* PTailNode = *pHead;
while(PTailNode->_pNext)
{
PTailNode = PTailNode->_pNext;
}
PTailNode->_pNext = BuyNode(data);
}
}
void PrintList(PNode pHead)
{
Node* pCurNode = pHead;
while(pCurNode)
{
printf("%d->", pCurNode->_data);
pCurNode = pCurNode->_pNext;
}
printf("NULL\n");
}
void PopBack(PNode* pHead)
{
assert(pHead);
if(*pHead == NULL)
{
return;
}
else if((*pHead)->_pNext == NULL)
{
free(pHead);
*pHead = NULL;
}
else
{
Node* PTailNode = *pHead;
Node* PCurNode = NULL;
while(PTailNode->_pNext)
{
PCurNode = PTailNode;
PTailNode = PTailNode->_pNext;
}
free(PCurNode->_pNext);
PCurNode->_pNext = NULL;
}
}
void PushFront(PNode* pHead, DataType data)
{
assert(pHead);
if(*pHead == NULL)
{
*pHead = BuyNode(data);
}
else
{
Node *PFrontNode = BuyNode(data);
PFrontNode->_pNext = *pHead;
*pHead = PFrontNode;
}
}
void PopFront(PNode* pHead)
{
assert(pHead);
if(*pHead == NULL)
{
return;
}
else if((*pHead)->_pNext == NULL)
{
*pHead = NULL;
}
else
{
Node * pDelNode = *pHead;
*pHead = (*pHead)->_pNext;
free(pDelNode);
}
}
Node* Find(PNode pHead, DataType data)
{
Node* PNewNode = pHead;
if(pHead == NULL)
{
return NULL;
}
while(PNewNode)
{
if(PNewNode->_data == data)
{
return PNewNode;
}
PNewNode = PNewNode->_pNext;
}
return NULL;
}
void Insert(PNode pos, DataType data)
{
if(pos == NULL)
{
return;
}
else
{
Node *PNewNode = BuyNode(data);
PNewNode->_pNext = pos->_pNext;
pos->_pNext = PNewNode;
}
}
void Erase(PNode* pHead, PNode pos)
{
assert(pHead);
assert(pos);
if(*pHead == NULL && pos == NULL)
{
return;
}
else if(*pHead == pos)
{
PopFront(pHead);
}
else
{
Node *PNewNode = *pHead;
while(PNewNode->_pNext != pos)
{
PNewNode = PNewNode->_pNext;
}
PNewNode->_pNext = pos->_pNext;
free(pos);
}
}
void PrintFromTail2Head(PNode pHead)
{
if(pHead == NULL)
{
return;
}
else
{
PrintFromTail2Head(pHead->_pNext);
printf("%d->",pHead->_data);
}
printf("\n");
}
void DeleteNotTailNode(PNode pos)
{
assert(pos);
if(pos == NULL || pos->_pNext == NULL)
{
return;
}
else
{
Node *PNewNode = pos->_pNext;
pos->_data = PNewNode->_data;
pos->_pNext = PNewNode->_pNext;
free(PNewNode);
}
}
void Remove(PNode* pHead, DataType data)
{
Node* ret = NULL;
Node* PNewNode = *pHead;
assert(pHead);
ret = Find(*pHead,data);
if(ret == NULL)
{
return;
}
else
{
Erase(pHead, ret);
}
}
void RemoveAll(PNode* pHead, DataType data)
{
Node* PNewNode = *pHead;
assert(pHead);
/*Remove(pHead,data);
ret = Find(*pHead,data);
if(ret == NULL)
{
return;
}
else
{
RemoveAll(pHead,data);
}*/
while(PNewNode->_pNext)
{
if(PNewNode->_data == data)
{
Node *PCurNode = NULL;
PCurNode = PNewNode->_pNext;
PNewNode->_data = PCurNode->_data;
PNewNode->_pNext = PCurNode->_pNext;
free(PCurNode);
}
else
{
PNewNode = PNewNode->_pNext;
}
}
if(PNewNode->_pNext == NULL)
{
Node* PTailNode = *pHead;
Node* PCurNode = NULL;
while(PTailNode->_pNext)
{
PCurNode = PTailNode;
PTailNode = PTailNode->_pNext;
}
free(PCurNode->_pNext);
PCurNode->_pNext = NULL;
}
}
size_t Size(PNode pHead)
{
int count = 0;
Node * PNewNode = pHead;
if(pHead == NULL)
{
return 0;
}
else
{
while(PNewNode)
{
count++;
PNewNode = PNewNode->_pNext;
}
}
return count;
}
int Empty(PNode pHead)
{
if(pHead == NULL)
{
return 1;
}
else
{
return 0;
}
}
PNode Back(PNode pHead)
{
if(pHead == NULL)
{
return NULL;
}
else
{
Node* PTailNode = pHead;
while(PTailNode->_pNext)
{
PTailNode = PTailNode->_pNext;
}
return PTailNode;
}
}
PNode Front(PNode pHead)
{
if(pHead == NULL)
{
return NULL;
}
else
{
return pHead;
}
}
test.c
#include "List.h"
void Test1()
{
Node *Node = NULL;
InitList(&Node);
PushBack(&Node, 1);
PushBack(&Node, 2);
PushBack(&Node, 3);
PushBack(&Node, 4);
PushBack(&Node, 5);
PushBack(&Node, 6);
PopBack(&Node);
PrintList(Node);
}
void Test2()
{
Node *Node = NULL;
InitList(&Node);
PushFront(&Node, 1);
PushFront(&Node, 2);
PushFront(&Node, 3);
PushFront(&Node, 4);
PushFront(&Node, 5);
PopFront(&Node);
PrintList(Node);
}
void Test3()
{
Node * Node = NULL;
PushFront(&Node, 1);
PushFront(&Node, 2);
PushFront(&Node, 3);
PushFront(&Node, 4);
PushFront(&Node, 5);
Insert(Find(Node,3),7);
PrintList(Node);
Erase(&Node,Find(Node,1));
Erase(&Node, Back(Node));
Erase(&Node, Front(Node));
PrintList(Node);
PrintFromTail2Head(Node);
Remove(&Node,5);
PrintList(Node);
}
void Test4()
{
Node * Node = NULL;
PushFront(&Node, 1);
PushFront(&Node, 7);
PushFront(&Node, 2);
PushFront(&Node, 1);
PushFront(&Node, 3);
PushFront(&Node, 4);
PushFront(&Node, 1);
PushFront(&Node, 6);
PushFront(&Node, 5);
PushFront(&Node, 1);
PrintList(Node);
RemoveAll(&Node, 1);
PrintList(Node);
printf("%d\n",Size(Node));
}
void Test5()
{
Node * Node = NULL;
int ret = 0;
PushFront(&Node, 1);
ret = Empty(Node);
if(ret == 1)
{
printf("单链表为空\n");
}
else
{
printf("单链表为非空\n");
}
}
int main()
{
Test3();
system("pause");
return 0;
}