单链表的实现
一、数据的定义
1. 定义数据和常数
定义一个结构体
typedef struct city{
int id;
char * name;
char * abbreviation;
}City, ElemType;
定义常量
#define TRUE 1
#define FALSE 0
#define NO_FOUND (-1)
#define NO_EXIST_ID (-999)
2. 定义结点和头结点
结点包括数据域和指针域
头结点可以记录单链表的长度
typedef struct node{
//数据域
ElemType city;
//指针域
struct node * next;
}Node;
typedef struct linkedList{
//头指针
Node * head;
//单链表的长度
int length;
}LinkedList;
二、链表的基本功能
//operation
//初始化
void InitList(LinkedList * linkedList, ElemType * dataArray, int length);
//创建新结点
Node * newNode(ElemType * elemType);
//插入:头插、尾插和中间任意位置插入
void InsertElem(LinkedList * linkedList, int pos, ElemType elemType);
//删除:包括头删、尾删和中间任意位置删除
ElemType DeleteElem(LinkedList * linkedList, int pos);
//修改
int UpdateElem(LinkedList * linkedList, int pos, ElemType elemType);
//查找
//按位查找
ElemType * GetElemByPosition(LinkedList * linkedList, int pos);
//按值查找
int GetElemByValue(LinkedList * linkedList, ElemType * elemType);
//返回求前驱结点
ElemType GetPreElem(LinkedList * linkedList, ElemType * elemType);
//返回后继结点
ElemType GetNextElem(LinkedList * linkedList, ElemType * elemType);
//返回链表的长度
int GetLength(LinkedList * linkedList);
//判断链表是否为空
int IsEmpty(LinkedList * linkedList);
//清空链表
void ClearLinkedList(LinkedList * linkedList);
//打印链表
void printLinkedList(LinkedList * linkedList);
三、链表的实现
1. 初始化
长度length为插入结点的个数,在单链表中,描述结点的位置是从1开始的,因此在循环插入的过程中,使用i + 1。
void InitList(LinkedList * linkedList, ElemType * dataArray, int length)
{
linkedList->head = NULL;
linkedList->length = 0;
if(length < 0 || !dataArray){
printf("初始化失败!\n");
return;
}
for (int i = 0; i < length; ++i) {
InsertElem(linkedList, i+1, dataArray[i]);
}
}
2. 插入结点
在位置为pos处插入,需要找到第pos - 1个结点。在第一次循环时,指针已经移到了第二个位置,因此要找到第pos-1个结点,需要循环pos-2次。注意: 链表的length要加1。
void InsertElem(LinkedList * linkedList, int pos, ElemType elemType)
{
/*Node * node = (Node *) malloc(sizeof(Node));
node->next = NULL;
node->city = elemType;*/
Node * node = newNode(&elemType);
Node * current = linkedList->head;
//1. 头插
if(pos == 1){
//1.1 链表为空
if(IsEmpty(linkedList) == TRUE){
linkedList->head = node;
linkedList->length++;
return;
}
//1.2 链表不为空
node->next = linkedList->head;
linkedList->head = node;
linkedList->length++;
return;
}
//2. 中间位置插入包括在结尾插入
for (int i = 1; current && i < pos - 1; ++i) {
current = current->next;
}
if(!current){
printf("插入失败!\n");
return;
}
node->next = current->next;
current->next = node;
linkedList->length++;
}
3. 删除结点
注意:在返回删除结点的数据后,链表的长度要减1,并且要释放被删除结点的内存空间。
ElemType DeleteElem(LinkedList * linkedList, int pos)
{
Node * current = linkedList->head;
ElemType data;
data.id = NO_EXIST_ID;
//头删
if(pos == 1){
data = current->city;
linkedList->head = current->next;
free(current);
linkedList->length--;
return data;
}
//任意位置删除(包括尾删)
for (int i = 1; current && i < (pos-1); ++i) {
current = current->next;
}
if(!current){
printf("删除失败!\n");
return data;
}
Node * del_node = current->next;
data = del_node->city;
current->next = del_node->next;
linkedList->length--;
free(del_node);
return data;
}
4. 修改结点
int UpdateElem(LinkedList * linkedList, int pos, ElemType elemType)
{
Node * current = linkedList->head;
if(!current){
printf("链表为空!修改失败!\n");
return FALSE;
}
for (int i = 1; current && i < pos; ++i) {
current = current->next;
}
if(!current){
return FALSE;
}
current->city = elemType;
return TRUE;
}
5. 查找结点
//按位查找
ElemType * GetElemByPosition(LinkedList * linkedList, int pos)
{
if(pos < 0 && pos > GetLength(linkedList)){
printf("位置不合法,查找失败!\n");
return NULL;
}
Node * current = linkedList->head;
for (int i = 1; current && i < pos; ++i) {
current = current->next;
}
if(!current){
printf("修改失败!\n");
return NULL;
}
return ¤t->city;
}
//按值查找
int GetElemByValue(LinkedList * linkedList, ElemType * elemType)
{
if(!linkedList->head || !elemType){
printf("查找失败!\n");
return NO_FOUND;
}
Node * current = linkedList->head;
int pos = 1;
while (current){
if(current->city.id == elemType->id)
if(current->city.name == elemType->name)
if(current->city.abbreviation == elemType->abbreviation)
return pos;
current = current->next;
pos++;
}
printf("未找到符合元素!\n");
return NO_FOUND;
}
6. 其他操作
//返回求前驱结点
ElemType GetPreElem(LinkedList * linkedList, ElemType * elemType)
{
ElemType data;
data.id = NO_EXIST_ID;
int pos = GetElemByValue(linkedList, elemType);
if(pos == 1){
printf("该结点为首元结点,没有前驱结点!\n");
return data;
}
Node * preNode = linkedList->head;
for (int i = 1; i < pos-1; ++i) {
preNode = preNode->next;
}
data = preNode -> city;
return data;
}
//返回后继结点
ElemType GetNextElem(LinkedList * linkedList, ElemType * elemType)
{
ElemType data;
data.id = NO_EXIST_ID;
int pos = GetElemByValue(linkedList, elemType);
if(pos == GetLength(linkedList)){
printf("该结点为尾元结点,没有后继结点!\n");
return data;
}
Node * nextNode = linkedList->head;
for (int i = 0; i < pos; ++i) {
nextNode = nextNode->next;
}
data = nextNode->city;
return data;
}
//创建新结点
Node * newNode(ElemType * elemType)
{
Node * node = (Node *) malloc(sizeof(Node));
node->city = *elemType;
node->next = NULL;
return node;
}
//清空链表
void ClearLinkedList(LinkedList * linkedList)
{
Node * preNode;
Node * current = linkedList->head;
while (current){
preNode = current;
current = current->next;
free(preNode);
}
linkedList->head = NULL;
linkedList->length = 0;
}
//返回链表的长度
int GetLength(LinkedList * linkedList)
{
return linkedList->length;
}
//判断链表是否为空
int IsEmpty(LinkedList * linkedList)
{
return linkedList->length == 0 ? TRUE : FALSE;
}
//打印链表
void printLinkedList(LinkedList * linkedList)
{
Node * current = linkedList->head;
if(!linkedList->head)
{
printf("链表为空!\n");
return;
}
while(current){
printf("[%d, %s, %s]\n", current->city.id, current->city.name, current->city.abbreviation);
current = current->next;
}
}
四、测试代码
1. 测试数据
City array[] = {
{1, "山西省", "晋"},
{2, "黑龙江省", "黑"},
{3, "吉林省", "吉"}
};
2. 测试函数
void testLinkedList()
{
printf("======== 初始化 ========\n");
printf("打印链表:\n");
LinkedList linkedList;
InitList(&linkedList, array, 3);
printLinkedList(&linkedList);
City city;
Node * node = (Node *) malloc(sizeof(Node));
node->next = NULL;
city.id = 4;
city.name = "四川省";
city.abbreviation = "川";
node->city = city;
//1. 插入
printf("\n======== 插入结点 ========\n");
InsertElem(&linkedList, 2, city);
printf("插入后:\n");
printLinkedList(&linkedList);
//2. 删除
ElemType del_elem = DeleteElem(&linkedList, 1);
printf("\n======== 删除结点 ========\n");
printf("删除首元结点后:\n");
printLinkedList(&linkedList);
printf("删除的元素是:[%d, %s, %s]\n", del_elem.id, del_elem.name, del_elem.abbreviation);
//3. 修改
printf("\n======== 修改结点 ========\n");
City city1 = {5, "江西省", "赣"};
UpdateElem(&linkedList, 2, city1) == TRUE ? printf("修改成功!\n") : printf("修改失败!\n");
printf("修改第二个结点后:\n");
printLinkedList(&linkedList);
//按值查找
printf("\n======== 按值查找 ========\n");
if(GetElemByValue(&linkedList, &city1) != NO_FOUND )
printf("city1 {5, \"江西省\", \"赣\"}的位置是:%d\n", GetElemByValue(&linkedList, &city1));
//按位查找
printf("\n======== 按位查找 ========\n");
ElemType * city2 = (ElemType *) malloc(sizeof(ElemType));
city2 = GetElemByPosition(&linkedList, 3);
printf("第三个位置的元素city2是:[%d, %s, %s]\n", city2->id, city2->name, city2->abbreviation);
//求前驱
printf("\n======== 求city2的前驱结点 ========\n");
ElemType city3 = GetPreElem(&linkedList, city2);
if(city3.id != NO_EXIST_ID)
printf("city2的前驱元素是:[%d, %s, %s]\n", city3.id, city3.name, city3.abbreviation);
//求后继
printf("\n======== 求city2的后继结点 ========\n");
ElemType city4 = GetNextElem(&linkedList, city2);
if(city4.id != NO_EXIST_ID)
printf("city2的后继元素是:[%d, %s, %s]\n", city4.id, city4.name, city4.abbreviation);
//清空链表
printf("\n======== 清空链表 ========\n");
ClearLinkedList(&linkedList);
printf("打印链表:\n");
printLinkedList(&linkedList);
}
3. 测试结果
======== 初始化 ========
打印链表:
[1, 山西省, 晋]
[2, 黑龙江省, 黑]
[3, 吉林省, 吉]
======== 插入结点 ========
插入后:
[1, 山西省, 晋]
[4, 四川省, 川]
[2, 黑龙江省, 黑]
[3, 吉林省, 吉]
======== 删除结点 ========
删除首元结点后:
[4, 四川省, 川]
[2, 黑龙江省, 黑]
[3, 吉林省, 吉]
删除的元素是:[1, 山西省, 晋]
======== 修改结点 ========
修改成功!
修改第二个结点后:
[4, 四川省, 川]
[5, 江西省, 赣]
[3, 吉林省, 吉]
======== 按值查找 ========
city1 {5, "江西省", "赣"}的位置是:2
======== 按位查找 ========
第三个位置的元素city2是:[3, 吉林省, 吉]
======== 求city2的前驱结点 ========
city2的前驱元素是:[5, 江西省, 赣]
======== 求city2的后继结点 ========
该结点为尾元结点,没有后继结点!
======== 清空链表 ========
打印链表:
链表为空!