数据结构之链表
最近,本学期陆陆续续学习了数据结构,学习了链表,栈,队列,二叉树。接下来,本段时间我会陆陆续续整理我本学期的笔记。小白一枚,写的不好很正常,部分也参考了别人的代码。
本节我会分成两个部分,一是线性表,而是顺序表。
首先我先讲讲线性表。
线性表不用多说了吧,简单说,就是先定义一个结构体,里面含有一个数据域,指针域。每个结点之间通过指针域进行连接。
顺序表和线性表的区别
什么是顺序表,和线性表差别在哪里?
简单来说,顺序表就是一个数组,也称为静态链表。顺序表在内存中,就是在连续的一段内存中存在,线性表在内存中则是零散分布的。那么线性表零散的内存是靠什么连接的呢,很简单,就是针指。每一个结点都会有一个数据域和指针域,就像一根线,将内存中的各个部分串联起来,访问了一个,根据这根线索,我们就可以访问下一个。
静态链表表面上没有用到针指,但是实际上从某种角度也实现了指针。顺序链表的每一个元素都由一个数据data,和一个下标cur组成(其实就相当于线性表的指针域)。静态链表相比于线性表,优点是插入和删除时,只需要删除游标,不需要移动元素。但是顺序链表很难解决长度的问题,多了浪费,少了不够。所以现实中,我们用的比较多的还是线性表。静态链表的话,我暂时还没写,如果有机会,以后我会补充。
单向线性表
我的LinkedList主要包括了几个功能,结点的插入,链表的初始化,链表的销毁,删除结点,遍历链表,查找结点,反转链表,判断是否成环,链表的结点两两进行交换,查找中间结点。
下面是头文件 linkedList.h,大家稍微看看即可,重心主要放在LinkedList.c
对了,这里为了方便管理,我们采用了二重指针的形式,就是一个有一个指针指向头节点。
头文件
#include<stdio.h>
#define OK 1
#define FALSE 0
/***************************************************************************************
* File Name : linkedList.h
* CopyRight : Chris
* SYSTEM : win10
* Create Data : 2020.3.28
*
*
*--------------------------------Revision History--------------------------------------
* No version Data Revised By Item Description
*
*
***************************************************************************************/
/**************************************************************
* Multi-Include-Prevent Section
**************************************************************/
#ifndef LINKEDLIST_H_INCLUDED
#define LINKEDLIST_H_INCLUDED
/**************************************************************
* Macro Define Section
**************************************************************/
#define OVERFLOW -1
/**************************************************************
* Struct Define Section
**************************************************************/
// define element type
typedef int ElemType;
// define struct of linked list
typedef struct LNode {
ElemType data;
struct LNode *next;
} LNode, *LinkedList;
// define Status
typedef enum Status {
ERROR,
SUCCESS
} Status;
/**************************************************************
* Prototype Declare Section
**************************************************************/
void pf(ElemType e);
/**
* @name : Status InitList(LinkList *L);
* @description : initialize an empty linked list with only the head node without value
* @param : L(the head node)
* @return : Status
* @notice : None
*/
Status InitList(LinkedList *L);
/**
* @name : void DestroyList(LinkedList *L)
* @description : destroy a linked list, free all the nodes
* @param : L(the head node)
* @return : None
* @notice : None
*/
void DestroyList(LinkedList *L);
/**
* @name : Status InsertList(LNode *p, LNode *q)
* @description : insert node q after node p
* @param : p, q
* @return : Status
* @notice : None
*/
Status InsertList(LNode *p, LNode *q);
/**
* @name : Status DeleteList(LNode *p, ElemType *e)
* @description : delete the first node after the node p and assign its value to e
* @param : p, e
* @return : Status
* @notice : None
*/
Status DeleteList(LNode *p, ElemType *e);
/**
* @name : void TraverseList(LinkedList L, void (*visit)(ElemType e))
* @description : traverse the linked list and call the funtion visit
* @param : L(the head node), visit
* @return : None
* @notice : None
*/
void TraverseList(LinkedList L, void (*visit)(ElemType e));
/**
* @name : Status SearchList(LinkedList L, ElemType e)
* @description : find the first node in the linked list according to e
* @param : L(the head node), e
* @return : Status
* @notice : None
*/
Status SearchList(LinkedList L, ElemType e);
/**
* @name : Status ReverseList(LinkedList *L)
* @description : reverse the linked list
* @param : L(the head node)
* @return : Status
* @notice : None
*/
Status ReverseList(LinkedList *L);
/**
* @name : Status IsLoopList(LinkedList L)
* @description : judge whether the linked list is looped
* @param : L(the head node)
* @return : Status
* @notice : None
*/
Status IsLoopList(LinkedList L);
/**
* @name : LNode* ReverseEvenList(LinkedList *L)
* @description : reverse the nodes which value is an even number in the linked list, input: 1 -> 2 -> 3 -> 4 output: 2 -> 1 -> 4 -> 3
* @param : L(the head node)
* @return : LNode(the new head node)
* @notice : choose to finish
*/
LNode* ReverseEvenList(LinkedList *L);
/**
* @name : LNode* FindMidNode(LinkedList *L)
* @description : find the middle node in the linked list
* @param : L(the head node)
* @return : LNode
* @notice : choose to finish
*/
LNode* FindMidNode(LinkedList *L);
/**
* @name : CreateList(LinkedList* L, int n)
* @description : Create a random list
* @param : L(the head node), n(num of the Node)
* @return : None
* @notice : None
*/
void CreateList(LinkedList* L, int n);
/**************************************************************
* End-Multi-Include-Prevent Section
**************************************************************/
#endif
源文件
#include "linkedList.h"
/**
* @name : Status InitList(LinkList *L);
* @description : initialize an empty linked list with only the head node without value
* @param : L(the head node)
* @return : Status
* @notice : None
*/
Status InitList(LinkedList *L) {
*L = (LinkedList)malloc(sizeof(LNode));
(*L)->next = NULL;
return OK;
}
/**
* @name : void DestroyList(LinkedList *L)
* @description : destroy a linked list, free all the nodes
* @param : L(the head node)
* @return : None
* @notice : None
*/
void DestroyList(LinkedList *L) {
LinkedList p, s;
p = (*L)->next;
while (p)
{
s = p->next;
free(p);
p = s;
}
(*L)->next = NULL;
return SUCCESS;
}
/**
* @name : Status InsertList(LNode *p, LNode *q)
* @description : insert node q after node p
* @param : p, q
* @return : Status
* @notice : None
*/
Status InsertList(LNode *p, LNode *q) {
if (p == NULL || q == NULL)
return ERROR;
q->next = p->next;
p->next = q;
return SUCCESS;
}
/**
* @name : Status DeleteList(LNode *p, ElemType *e)
* @description : delete the first node after the node p and assign its value to e
* @param : p, e
* @return : Status
* @notice : None
*/
Status DeleteList(LNode *p, ElemType *e) {
LinkedList r;
r = p->next;
p->next = p->next->next;
*e = r->data;
free(r);
return SUCCESS;
}
/**
* @name : void TraverseList(LinkedList L, void (*visit)(ElemType e))
* @description : traverse the linked list and call the funtion visit
* @param : L(the head node), visit
* @return : None
* @notice : None
*/
void TraverseList(LinkedList L, void (*visit)(ElemType e)) {
LinkedList p = L->next;
while (p) {
visit(p->data);
p = p->next;
}
printf("\n");
}
void pf(ElemType e) {
printf("%d ", e);
}
/**
* @name : Status SearchList(LinkedList L, ElemType e)
* @description : find the first node in the linked list according to e
* @param : L(the head node), e
* @return : Status
* @notice : None
*/
Status SearchList(LinkedList L, ElemType e) {
LinkedList p;
p = L->next;
while(p)
{
if (p->data == e)
return SUCCESS;
p = p->next;
}
return ERROR;
}
/**
* @name : Status ReverseList(LinkedList *L)
* @description : reverse the linked list
* @param : L(the head node)
* @return : Status
* @notice : None
*/
Status ReverseList(LinkedList *L) {
LinkedList p,r, q;
p = NULL;
r = (*L)->next;
q = r->next;
if (r == NULL || q == NULL)/*判断是否为空表或者只有一个节点*/
return;
while (q) {
r->next = p;
p = r;
r = q;
q = q->next;
}
r->next = p;
(*L)->next = r;
printf("\n");
return SUCCESS;
}
/**
* @name : Status IsLoopList(LinkedList L)
* @description : judge whether the linked list is looped
* @param : L(the head node)
* @return : Status
* @notice : None
*/
Status IsLoopList(LinkedList L) {
if (L == NULL || L->next == NULL)
return ERROR;
LinkedList fast, slow;
fast = L->next->next;
slow = L->next;
while (1) {
if (!fast || !slow)
return ERROR;
if (fast == slow)
return SUCCESS;
else {
fast = fast->next->next;
slow = slow->next;
}
}
}
/**
* @name : LNode* ReverseEvenList(LinkedList *L)
* @description : reverse the nodes which value is an even number in the linked list, input: 1 -> 2 -> 3 -> 4 output: 2 -> 1 -> 4 -> 3
* @param : L(the head node)
* @return : LNode(the new head node)
* @notice : choose to finish
*/
LNode* ReverseEvenList(LinkedList* L) {
LinkedList dummyHead = (LinkedList)malloc(sizeof(LNode));//创建虚拟结点
dummyHead->next = (*L)->next;//虚拟结点的next指向联保第一个结点
LinkedList cur = dummyHead;
while (cur->next != NULL && cur->next->next != NULL) {
LinkedList tmp = cur->next; // 记录临时节点
LinkedList tmp1 = cur->next->next->next; // 记录临时节点
cur->next = cur->next->next; // 步骤一
cur->next->next = tmp; // 步骤二
cur->next->next->next = tmp1; // 步骤三
cur = cur->next->next; // cur移动两位,准备下一轮交换
}
LinkedList p = dummyHead->next;
free(dummyHead);
return p;
}
/**
* @name : LNode* FindMidNode(LinkedList *L)
* @description : find the middle node in the linked list
* @param : L(the head node)
* @return : LNode
* @notice : choose to finish
*/
LNode* FindMidNode(LinkedList* L) {
LinkedList fast, slow, p;
fast = *L;
slow = *L;
while (fast && fast->next)
{
p = slow;
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
void CreateList(LinkedList* L, int n)
{
srand(time(0));//初始化随机种子
int i;
*L = (LinkedList)malloc(sizeof(LNode));
(*L)->next = NULL;
LinkedList p;
for (i = 0; i < n; i++)
{
p = (LinkedList)malloc(sizeof(LNode));
p->data = rand() % 100 + 1;//生成1-100的随机数;
p->next = (*L)->next;
(*L)->next = p;
}
}
void Display(LinkedList* L)//打印链表
{
LinkedList p;
p = (*L)->next;
while (p)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
前面的大部分功能都挺简单,不细讲,主要讲我当时觉得写起来有困难的部分。判断是否成环,就是类似于中学学习的追击问题。我们可以多设置一个指针,已两倍的速度行走,就是每次移动两次,p=p->next->next
,到最后我们如果此指针与正常遍历的指针相遇,即成环。找到中间结点也是一样,我们也是用两个指针,一快一慢,快的二倍速行走,当快的指针走到末尾,那么慢的指针指向的就是中间结点。
好了,到了我认为最难的部分,ReverseEvenList,结点间两两进行交换,首先,我们需要创造一个虚拟的结点。
大概画了画原理图(丑了点,凑合看),就是cur结点先指向结点2,然后结点2又指向结点1,结点1最后指向结点1,可以看到,1、2已经交换完成,这只是一次循环。对了,最后别忘记free掉虚拟结点。
还有一个有趣的是,是遍历链表,我们看看这个函数,void TraverseList(LinkedList L, void (*visit)(ElemType e))
,本人菜鸡一枚,上课没认真听老师讲指针,居然漏了函数指针这个东西。对于遍历链表,我们每一次遍历可能都会有不同的操作,例如本次只是简单打印一下结点的值。为了方便,不必重复修改代码,我们采用了函数指针。函数指针,顾名思义就是一个指向函数的指针。在该遍历函数的调用中,我们可以传入一个有打印功能的函数,那么遍历该链表时,我们对每一个结点的操作就是打印该结点的内容。当然也可以换成其他的。
双向线性表
对于双向链表,实现的功能比单向的少一点,概念了解就行。
头文件
#include "duLinkedList.h"
#include "malloc.h"
/**
* @name : Status InitList_DuL(DuLinkedList *L)
* @description : initialize an empty linked list with only the head node
* @param : L(the head node)
* @return : Status
* @notice : None
*/
Status InitList_DuL(DuLinkedList *L) {
*L = (DuLinkedList)malloc(sizeof(DuLNode));
(*L)->next = NULL;
(*L)->prior = NULL;
(*L)->data = NULL;
return SUCCESS;
}
/**
* @name : void DestroyList_DuL(DuLinkedList *L)
* @description : destroy a linked list
* @param : L(the head node)
* @return : status
* @notice : None
*/
void DestroyList_DuL(DuLinkedList *L) {
DuLinkedList p;
while (*L)
{
p = (*L)->next;
free(*L);
*L = p;
if (p)
p->prior = NULL;
}
return SUCCESS;
}
/**
* @name : Status InsertBeforeList_DuL(DuLNode *p, LNode *q)
* @description : insert node q before node p
* @param : p, q
* @return : status
* @notice : None
*/
Status InsertBeforeList_DuL(DuLNode *p, DuLNode *q) {
if (!p)
return ERROR;
if (p->prior) {
p->prior->next = q;
q->prior = p->prior;
}
q->next = p;
p->prior = q;
return SUCCESS;
}
/**
* @name : Status InsertAfterList_DuL(DuLNode *p, DuLNode *q)
* @description : insert node q after node p
* @param : p, q
* @return : status
* @notice : None
*/
Status InsertAfterList_DuL(DuLNode *p, DuLNode *q) {
if (p->next == NULL&&p->prior==NULL)
{
q->prior = NULL;
q->next = p;
p->prior = q;
}
else
{
q->next = p->next;
q->prior = p;
p->next->prior = q;
p->next = q;
}
return SUCCESS;
}
/**
* @name : Status DeleteList_DuL(DuLNode *p, ElemType *e)
* @description : delete the first node after the node p and assign its value to e
* @param : p, e
* @return : status
* @notice : None
*/
Status DeleteList_DuL(DuLNode *p, ElemType *e) {
*e = p->next->data;
p = p->next;
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
return SUCCESS;
}
/**
* @name : void TraverseList_DuL(DuLinkedList L, void (*visit)(ElemType e))
* @description : traverse the linked list and call the funtion visit
* @param : L(the head node), visit
* @return : Status
* @notice : None
*/
void TraverseList_DuL(DuLinkedList L, void (*visit)(ElemType e)) {
DuLinkedList p = L;
while (p) {
visit(p->data);
p = p->next;
}
printf("\n");
}
void pf(ElemType e) {
printf("%d ", e);
}
void Display(DuLinkedList * L)//打印链表
{
DuLinkedList p = *L;
do {
if (p == NULL)
{
printf("The list is empty\n");
break;
}
printf("%d ", p->data);
p = p->next;
} while (p);
printf("\n");
}
void CreateList(DuLinkedList *L)
{
srand(time(0));//初始化随机种子
for (int i = 0; i < 5; i++)
{
DuLinkedList r = (DuLinkedList)malloc(sizeof(DuLNode));
r->prior = NULL;
r->data= rand() % 100 + 1;//生成1-100的随机数;
DuLinkedList p = *L;
r->next = p;
p->prior = r;
*L = r;
}
}
源文件
#include "duLinkedList.h"
#include "malloc.h"
/**
* @name : Status InitList_DuL(DuLinkedList *L)
* @description : initialize an empty linked list with only the head node
* @param : L(the head node)
* @return : Status
* @notice : None
*/
Status InitList_DuL(DuLinkedList *L) {
*L = (DuLinkedList)malloc(sizeof(DuLNode));
(*L)->next = NULL;
(*L)->prior = NULL;
(*L)->data = NULL;
return SUCCESS;
}
/**
* @name : void DestroyList_DuL(DuLinkedList *L)
* @description : destroy a linked list
* @param : L(the head node)
* @return : status
* @notice : None
*/
void DestroyList_DuL(DuLinkedList *L) {
DuLinkedList p;
while (*L)
{
p = (*L)->next;
free(*L);
*L = p;
if (p)
p->prior = NULL;
}
return SUCCESS;
}
/**
* @name : Status InsertBeforeList_DuL(DuLNode *p, LNode *q)
* @description : insert node q before node p
* @param : p, q
* @return : status
* @notice : None
*/
Status InsertBeforeList_DuL(DuLNode *p, DuLNode *q) {
if (!p)
return ERROR;
if (p->prior) {
p->prior->next = q;
q->prior = p->prior;
}
q->next = p;
p->prior = q;
return SUCCESS;
}
/**
* @name : Status InsertAfterList_DuL(DuLNode *p, DuLNode *q)
* @description : insert node q after node p
* @param : p, q
* @return : status
* @notice : None
*/
Status InsertAfterList_DuL(DuLNode *p, DuLNode *q) {
if (p->next == NULL&&p->prior==NULL)
{
q->prior = NULL;
q->next = p;
p->prior = q;
}
else
{
q->next = p->next;
q->prior = p;
p->next->prior = q;
p->next = q;
}
return SUCCESS;
}
/**
* @name : Status DeleteList_DuL(DuLNode *p, ElemType *e)
* @description : delete the first node after the node p and assign its value to e
* @param : p, e
* @return : status
* @notice : None
*/
Status DeleteList_DuL(DuLNode *p, ElemType *e) {
*e = p->next->data;
p = p->next;
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
return SUCCESS;
}
/**
* @name : void TraverseList_DuL(DuLinkedList L, void (*visit)(ElemType e))
* @description : traverse the linked list and call the funtion visit
* @param : L(the head node), visit
* @return : Status
* @notice : None
*/
void TraverseList_DuL(DuLinkedList L, void (*visit)(ElemType e)) {
DuLinkedList p = L;
while (p) {
visit(p->data);
p = p->next;
}
printf("\n");
}
void pf(ElemType e) {
printf("%d ", e);
}
void Display(DuLinkedList * L)//打印链表
{
DuLinkedList p = *L;
do {
if (p == NULL)
{
printf("The list is empty\n");
break;
}
printf("%d ", p->data);
p = p->next;
} while (p);
printf("\n");
}
void CreateList(DuLinkedList *L)
{
srand(time(0));//初始化随机种子
for (int i = 0; i < 5; i++)
{
DuLinkedList r = (DuLinkedList)malloc(sizeof(DuLNode));
r->prior = NULL;
r->data= rand() % 100 + 1;//生成1-100的随机数;
DuLinkedList p = *L;
r->next = p;
p->prior = r;
*L = r;
}
}
双向链表我们主要是要注意有两个指针,插入修改删除都有些不一样,一定要注意指针。双向链表的实现其实也差不多,我就不画图了(懒)。
大一新生一枚,代码可能有很多出错且不严谨的地方,仅供参考。