title: 头指针单向链表基本操作
date: 2020-10-04 16:47:34
tags: 单链表基本操作
categories: 数据结构
感觉每次自己提前写的东西都和老师讲的书上内容不太一样= =
当然大多数是老师讲的更正规一些,毕竟我只是一个会了链表本质就瞎玩的人
属于自己琢磨,自己造的那种,很多东西也是不怎么规范
但是有的东西我就觉得自己的更好
比如下面所说…
课本上写的是以头指针为基本单位,每次开一个头指针,即可视为开辟了一个链表。
而我参考了动态数组静态数组的写法,将头指针作为了结构体中的一个成员变量,另一个成员变量存储这个链表的长度。每次开一个结构体变量,视为开辟了一个链表。
谈一下我的做法的优越性:
在插入删除的时候,我可以在一开始就判断要插入或删除的结点序号是否存在,因为我有链表的长度,如果结点序号非法,那么直接终止就可以了。然而书上的做法,它只有一个头指针(头结点),因此在插入删除的时候需要先去遍历,至于结点序号存不存在则需要在遍历完之后才能知道。
#include <stdio.h>
#include <stdlib.h>
// 在链表尾添加的结点中数据域的内容data
#define TAIL_ADD_NODE_DATA_NUMBER 6
// 要删除的结点的序列号
#define DELETED_NODE_SERIAL 1
// 要删除的结点中存的数据
#define DELETED_NODE_DATA 1
// 初始化的链表长度
#define INITIAL_LENGTH 5
typedef struct list
{
int data;
struct list *next;
} Node, *List;
typedef struct pHead
{
// 一个头指针可以确定一个链表
List pHead;
// 这里标上链表的长度可以省去很多麻烦
int length;
} LinkList;
void Print();
void PrintList(LinkList);
void InitializeList(LinkList &);
// 在链表尾添加一个结点
void AddTail(LinkList &, int);
// 插入一个 数据域 data = n的结点, 并且使之成为链表中第i个结点
void InsertNode(LinkList *, int i, int n);
// 删除第 i 个结点
void DeleteNodeBySerialNumber(LinkList *, int i);
// 删除 data = n 的结点
void DeleteNodeByContainedData(LinkList &, int);
// 释放链表
void FreeList(LinkList &);
int main(void)
{
// 每使用一次LinkList,就相当于开辟了一个链表
LinkList list;
// 初始化链表
list.pHead = NULL;
list.length = 0;
InitializeList(list);
PrintList(list);
AddTail(list, TAIL_ADD_NODE_DATA_NUMBER);
PrintList(list);
DeleteNodeBySerialNumber(&list, DELETED_NODE_SERIAL);
PrintList(list);
DeleteNodeBySerialNumber(&list, 2);
PrintList(list);
InsertNode(&list, 1, 7);
PrintList(list);
InsertNode(&list, 3, 8);
PrintList(list);
InsertNode(&list, 7, 100);
PrintList(list);
DeleteNodeBySerialNumber(&list, 7);
PrintList(list);
FreeList(list);
PrintList(list);
}
void Print()
{
printf("******************************************\n");
}
void PrintList(LinkList list)
{
List temp = list.pHead;
printf("当前链表长度为 %d\n", list.length);
if(list.length == 0)
return;
printf("打印链表内容\n");
while (temp != NULL)
{
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
Print();
}
void InitializeList(LinkList &list)
{
printf("初始化链表...\n");
List temp = NULL;
for (int i = 0; i < INITIAL_LENGTH; i++)
{
List p = (List)malloc(sizeof(Node));
p->next = NULL;
p->data = i + 1;
if (i == 0)
list.pHead = p;
else
temp->next = p;
temp = p;
}
list.length = INITIAL_LENGTH;
}
void AddTail(LinkList &list, int data)
{
printf("向链表末尾添加 数据域data = %d的结点...\n", data);
List p = (List)malloc(sizeof(Node));
p->data = data;
p->next = NULL;
// 检查链表是否为空,如果是空链表,则需要改变头指针
if (list.pHead == NULL)
list.pHead->next = p;
else
{
List temp = list.pHead;
while (temp->next != NULL)
temp = temp->next;
temp->next = p;
}
list.length++;
}
void DeleteNodeBySerialNumber(LinkList *pList, int serial)
{
if (!((serial >= 1) && (serial <= pList->length)))
exit(EXIT_FAILURE);
printf("删除第%d个结点...", serial);
List deletedNode = NULL;
// 如果只删除第一个,要删除的结点是没有上一个结点的,其实找上一个
// 结点的意义也就无非是要找到这个结点的地址,然后改变上一个结点中指针域
// 的指向,让上一个结点指向被删除的结点指向的结点。
// 综上所述,删除结点就是要找到前指针。
if (serial == 1)
{
deletedNode = pList->pHead;
pList->pHead = pList->pHead->next;
}
else
{
List temp = pList->pHead;
// 请细细思考for循环中为什么初始化i = 1
// 并且用 i < serial - 1 作为判断条件
// 其中有坑
for (int i = 1; i < serial - 1; i++)
temp = temp->next;
deletedNode = temp->next;
temp->next = temp->next->next;
}
free(deletedNode);
pList->length--;
}
void InsertNode(LinkList *pList, int serial, int data)
{
printf("在第%d号位插入一个数据域为data = %d的结点...\n", serial, data);
if (!((serial >= 1) && (serial <= pList->length + 1)))
exit(EXIT_FAILURE);
List p = (List)malloc(sizeof(Node));
p->data = data;
p->next = NULL;
if (serial == 1)
{
p->next = pList->pHead;
pList->pHead = p;
}
else
{
List temp = pList->pHead;
for (int i = 1; i < serial - 1; i++)
temp = temp->next;
p->next = temp->next;
temp->next = p;
}
pList->length++;
}
void FreeList(LinkList &list)
{
printf("释放链表在堆区开辟的所有空间...\n");
while (list.pHead != NULL)
{
List deletedNode = list.pHead;
list.pHead = (list.pHead)->next;
free(deletedNode);
}
list.length = 0;
}