文章目录
单链表
一、单链表的定义
由于顺序表的插入删除操作需要移动大量的元素,影响了运行效率,因此引入了线性表的链式存储——单链表。
二、线性表的链式存储结构
线性表的链式存储结构:线性表中数据元素(结点)在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理位置上不一定相邻。
三、单链表的实现
单链表存储结构代码
typedef struct Node
{
char data;
struct Node* next;
} Node,*LinkList;
初始化单链表
//初始化单链表
LinkList initLinkList()
{
Node* LinkHead = (Node*)malloc(sizeof(Node));
LinkHead->data = '\0';
LinkHead->next = NULL;
return LinkHead;
}
打印单链表
//打印单链表
void printList(Node* LinkHead)
{
Node* p = LinkHead->next;
while(p != NULL)
{
printf("%c",p->data);
p = p->next;
}
printf("\n\n");
}
向单链表中添加元素
//向单链表中添加元素
void addElement(Node* LinkHead,char addChar)
{
Node* p,*q;
q = (Node*)malloc(sizeof(Node));
q->data = addChar;
q->next = NULL;
p = LinkHead;
while(p->next != NULL)
{
p = p->next;
}
p->next = q;
}
单链表的读取
思路:
(1)声明一个指针 p 指向链表第一个节点,初始化 j 从 1 开始;
(2)当j < i时,遍历链表,让p的指针向后移动,不断指向下一节点,j 累加1;
(3)若到链表末尾 p 为空,则说明第 i 个节点不存在;
(4)否则查找成功,打印节点 p 的数据。
代码如下:
//单链表的读取(打印单链表中第i个数据元素的值)
void GetElement(Node* LinkHead, int i)
{
Node* p;
p = LinkHead->next;
int j = 1;
while(p != NULL && j < i)
{
p = p->next;
++j;
}
if(p == NULL || j > i)
{
printf("Can't get it !\n\n");
return ;
}
printf("The element at its %d-th position is %c\n\n",i,p->data);
}
单链表的插入
设储存元素 e 的节点为 q ,若要实现将节点 q 插入到节点 p 和 p->next 之间,只需要让 q->next和 p->next 的指针做一点改变即可。
代码如下:
//单链表的插入
void ListInsert(Node* LinkHead, int position, char InsertChar)
{
if(position <= 0)
{
printf("The position %d out of range of linked list!\n\n",position);
return ;
}
Node* p, *q;
p = LinkHead;
for(int i = 0; i < position; ++i)
{
p = p->next;
if(p == NULL)
{
printf("The position %d out of range of linked list!\n\n ",position);
return ;
}
}
q = (Node*)malloc(sizeof(Node));
q->data = InsertChar;
q->next = p->next;
p->next = q;
}
单链表的删除
设存储元素 ai 的节点为 q,要实现将节点 q 删除单链表的操作,其实就是将它的前继节点的指针绕过,指向它的后继节点即可。
代码如下:
//单链表的删除(删除第i个数据元素)
void ListDelete(Node* LinkHead, int i)
{
int j = 1;
Node *p, *q;
p = LinkHead;
while(p->next != NULL && j < i)
{
p = p->next;
++j;
}
if(p->next == NULL || j > i)
{
printf("Can't delete it!\n\n");
return ;
}
q = p->next;
p ->next = q->next;
free(q);
}
单链表的整表删除
当我们不打算使用这个单链表时,我们需要把它销毁,即在内存中将其释放,思路如下:
(1)声明一指针 p 和 q
(2)将第一个节点赋值给 p
(3)循环:
① 将下一节点赋值给 q ;
② 释放 p ;
③ 将 q 赋值给 p 。
代码如下:
//单链表的整表删除
void ClearList(Node* LinkHead)
{
Node *q,*p;
p = LinkHead->next;
while(p != NULL)
{
q = p->next;
free(p);
p = q;
}
LinkHead->next = NULL;
}
完整代码
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
char data;
struct Node* next;
} Node,*LinkList;
//初始化单链表
LinkList initLinkList()
{
Node* LinkHead = (Node*)malloc(sizeof(Node));
LinkHead->data = '\0';
LinkHead->next = NULL;
return LinkHead;
}
//打印单链表
void printList(Node* LinkHead)
{
Node* p = LinkHead->next;
while(p != NULL)
{
printf("%c",p->data);
p = p->next;
}
printf("\n\n");
}
//向单链表中添加元素
void addElement(Node* LinkHead,char addChar)
{
Node* p,*q;
q = (Node*)malloc(sizeof(Node));
q->data = addChar;
q->next = NULL;
p = LinkHead;
while(p->next != NULL)
{
p = p->next;
}
p->next = q;
}
//单链表的读取(打印单链表中第i个数据元素的值)
void GetElement(Node* LinkHead, int i)
{
Node* p;
p = LinkHead->next;
int j = 1;
while(p != NULL && j < i)
{
p = p->next;
++j;
}
if(p == NULL || j > i)
{
printf("Can't get it !\n\n");
return ;
}
printf("The element at its %d-th position is %c\n\n",i,p->data);
}
//单链表的插入
void ListInsert(Node* LinkHead, int position, char InsertChar)
{
if(position <= 0)
{
printf("The position %d out of range of linked list!\n\n",position);
return ;
}
Node* p, *q;
p = LinkHead;
for(int i = 0; i < position; ++i)
{
p = p->next;
if(p == NULL)
{
printf("The position %d out of range of linked list!\n\n ",position);
return ;
}
}
q = (Node*)malloc(sizeof(Node));
q->data = InsertChar;
q->next = p->next;
p->next = q;
}
//单链表的删除(删除第i个数据元素)
void ListDelete(Node* LinkHead, int i)
{
int j = 1;
Node *p, *q;
p = LinkHead;
while(p->next != NULL && j < i)
{
p = p->next;
++j;
}
if(p->next == NULL || j > i)
{
printf("Can't delete it!\n\n");
return ;
}
q = p->next;
p ->next = q->next;
free(q);
}
//单链表的整表删除
void ClearList(Node* LinkHead)
{
Node *q,*p;
p = LinkHead->next;
while(p != NULL)
{
q = p->next;
free(p);
p = q;
}
LinkHead->next = NULL;
}
//测试
void test()
{
LinkList tempLinkList = initLinkList();
printList(tempLinkList);
//添加元素测试
printf("--------------addElementTest--------------\n\n");
addElement(tempLinkList,'I');
addElement(tempLinkList,' ');
addElement(tempLinkList,'l');
addElement(tempLinkList,'o');
addElement(tempLinkList,'v');
addElement(tempLinkList,'e');
addElement(tempLinkList,' ');
addElement(tempLinkList,'d');
addElement(tempLinkList,'a');
addElement(tempLinkList,'t');
addElement(tempLinkList,'a');
addElement(tempLinkList,'!');
printList(tempLinkList);
//读取测试
printf("--------------GetElementTest--------------\n\n");
GetElement(tempLinkList,1);
//插入测试
printf("--------------ListInsertTest--------------\n\n");
ListInsert(tempLinkList,12,'!');
ListInsert(tempLinkList,14,'!');
ListInsert(tempLinkList,-1,'!');
printList(tempLinkList);
//删除测试
printf("--------------ListDeleteTest--------------\n\n");
ListDelete(tempLinkList,13);
ListDelete(tempLinkList,15);
printList(tempLinkList);
//清空测试
printf("--------------ClearListTest--------------\n\n");
ClearList(tempLinkList);
printList(tempLinkList);
}
int main()
{
test();
}
运行截图
闵版代码(略改)
#include <stdio.h>
#include <malloc.h>
/*
Linked list of characters. The key is data.
*/
typedef struct LinkNode{
char data;
struct LinkNode* next;
} LNode,*LinkList,*NodePtr;
/*
Initialize the list with a header.
@return The pointer to the header.
*/
LinkList initLinkList()
{
NodePtr tempHeader = (NodePtr)malloc(sizeof(LNode));
tempHeader->data = '\0';
tempHeader->next = NULL;
return tempHeader;
} // Off initLinkList
/*
Print the list.
@param paraHeader The header of the list.
*/
void printList(NodePtr paraHeader)
{
NodePtr p = paraHeader->next;
while(p != NULL)
{
printf("%c",p->data);
p = p->next;
}printf("\r\n");
} //Off printList
/*
Add an element to the tail.
@param paraHeader The header of the list.
@param paraChar the given char.
*/
void appendElement(NodePtr paraHeader, char paraChar)
{
NodePtr p,q;
// Step 1. Construct a new node.
q = (NodePtr)malloc(sizeof(LNode));
q->data = paraChar;
q->next = NULL;
// Step 2. Search to the tail.
p = paraHeader;
while (p->next != NULL)
{
p = p->next;
} //Off while
// Step 3. Now add/link.
p->next = q;
} //Off appendElement
/*
Insert an element to the given position.
@param paraHeader The header of the list.
@param paraChar The given char.
@param paraPosition The given position.
*/
void insertElement(NodePtr paraHeader,char paraChar,int paraPositon)
{
NodePtr p,q;
// Step 1. Search to the position.
p = paraHeader;
for(int i = 0; i < paraPositon; ++i)
{
p = p->next;
if(p == NULL)
{
printf("The position %d is beyond the scope of the list.",paraPositon);
return ;
} //Off if
} //Off for i
// Step 2. Construct a new node.
q = (NodePtr)malloc(sizeof(LNode));
q->data = paraChar;
// Step 3. Now link.
printf("linking\r\n");
q->next = p->next;
p->next = q;
} //Off insertElement
/*
Delete an element from the list.
@param paraHeader The header of the list.
@param paraChar The given char.
*/
void deleteElement(NodePtr paraHeader,char paraChar)
{
NodePtr p,q;
p = paraHeader;
while((p->next != NULL) && (p->next->data != paraChar))
{
p = p->next;
} //Off while
if(p->next == NULL)
{
printf("Can't delete %c\r\n",paraChar);
return ;
} //Off if
q = p->next;
p->next = q->next;
free(q);
} //Off deleteElement
/*
Unit test
*/
void appendInsertDeleteTest()
{
// Step 1. Initialize an empty list.
LinkList tempList = initLinkList();
printList(tempList);
// Step 2. Add some characters.
appendElement(tempList,'H');
appendElement(tempList,'e');
appendElement(tempList,'l');
appendElement(tempList,'l');
appendElement(tempList,'o');
appendElement(tempList,'!');
printList(tempList);
//Step 3. Delete some characters(the first occurence).
deleteElement(tempList,'e');
deleteElement(tempList,'a');
deleteElement(tempList,'o');
printList(tempList);
// Step 4. Insert to a given position.
insertElement(tempList,'o',1);
printList(tempList);
} //Off appendInsertDeleteTest
/*
Address test: beyond the book.
*/
void basicAddressTest()
{
LNode tempNode1,tempNode2;
tempNode1.data = 4;
tempNode1.next = NULL;
tempNode2.data = 6;
tempNode2.next = NULL;
printf("The first node: %p, %p, %p\r\n",&tempNode1,&tempNode1.data,&tempNode1.next);
printf("The second node: %p, %p, %p\r\n",&tempNode2,&tempNode2.data,&tempNode2.next);
} //Off basicAddressTest
/*
The entrance.
*/
int main()
{
appendInsertDeleteTest();
basicAddressTest();
} //Off main
四、总结
单链表与顺序表的简单对比:
-
若线性表需要频繁查找,很少进行插入及删除操作时,宜采用顺序表
-
当线性表中元素格式变化较大或不知道有多大时,最好采用单链表
总之,单链表和顺序表各有其优缺点,不能简单评判其好坏,应当根据实际情况,综合考虑使用哪种数据结构更优。