文章目录
- 单链表(含头结点)
- 一、单链表
- 二、单链表的创建(有头结点)
- 三、单链表的结点查找(按位置查找)
- 四、单链表的插入操作
- 五、链表的删除操作
- 六、链表的逆置
- 七、链表的置空
- 八、链表的销毁
单链表(含头结点)
一、单链表
- 1.1 关于单链表
单链表是一种采用链式存储的线性结构,其数据元素是由结点结构体组成,结点包括数据域和指针域。
相较于顺序表来说,单链表解脱了需要整片存储空间和存储空间大小固定的束缚;解决了插入、删除操作时对其他元素的操作问题。
相应的对于顺序表来说,单链表的整体存储空间大小需求更多;同时查询数据元素的方法也较单一。 - 2.1 单链表的结构体
单链表的结构体是由数据域和指针域组成的,数据域存放要保存的数据,指针域保存下一个结点的地址(下一个结点为空,保存NULL)。
typedef struct LNode
{int data;struct LNode *next;
}linklist_t, LNode_t;
二、单链表的创建(有头结点)
- 2.1 传参的方式创建单链表
void Create_Linklist(linklist_t **L){//先判断传入的参数是否正确if(NULL == L){printf("Parameter error\n");return;}//创建头结点*L = (linklist_t *)malloc(sizeof(linklist_t);if(NULL == *L){printf("Space request error\n");return;}(*L)->data = -1;(*L)->next = NULL;return;
}
- 2.2 指针函数的方式创建单链表
linklist *Create_Linklist(void){linklist *L = (linklist *)malloc(sizeof(linklist));if(NULL == L){printf("Space request error\n");return NULL;}L->data = -1;L->next = NULL;return L;
}
- 上述两种方法的区别
- 上述的两种方法仅仅是创建了带有头结点的单链表,想要得到想要的单链表还需要使用单链表的插入函数。
三、单链表的结点查找(按位置查找)
- 3.1 单链表中元素的个数
功能:统计单链表中元素个数
参数:单链表的头结点
返回值:成功返回单链表中元素个数;失败返回-1
//用于记录单链表中元素的个数
int Length_Linklist(linklist_t *L){//判断参数是否正确if(NULL == L){printf("Parameter error\n");return -1;}//num记录元素个数int num = 0;if(NULL == L->next){return num;}LNode_t *Q = L->next; num = 1;while(NULL != Q->next){Q = Q->next;num++;}return num;
}
- 3.2 单链表的查找
功能:查找单链表的指定结点的前一个结点
参数:单链表的头指针、需要查找结点的位置参数
返回值:成功返回的为该结点的前一个结点的结点指针;失败返回NULL
//如果第一个有效结点算作第 0 位则pos不用改动,如果第一个有效结点算作第 1 位,则pos = pos - 1;
//返回的为该结点的前一个结点的结点指针,主要也是为了方便之后的插入和删除操作
LNode_t *PosFind_Linklist(linklist_t *L, int pos){//判断参数是否正确if(NULL == L){printf("Parameter error\n");return NULL;}//注意这里是 "pos > num",可能会返回的是尾结点(pos = num),\这样写也主要是为了便于之后插入的方便,若只想查找元素数据可在该函数前加一个判断int Length_Linklist(L);if((pos < 0) || (pos > num)){printf("Parameter error\n");return NULL;}num = 0;LNode_t *N = L;while(num != pos){num++;N = N->next;}return N;
}
四、单链表的插入操作
- 4.1 头插法
int HeadInsert_Linklist(linklist_t *L, int value){//判断参数是否正确if(NULL == L){printf("Parameter error\n");return 0;}//创建结点LNode_t *N = (LNode *)malloc(sizeof(LNode_t));N->data = value;N->next = NULL;//插入到头结点之后N->next = L->next;L->next = N;return 0;
}
- 4.2 尾插法
int TailInsert_Linklist(linklist_t *L, int value){//判断参数是否正确if(NULL == L){printf("Parameter error\n");return -1;}//创建结点LNode_t *N = (LNode *)malloc(sizeof(LNode_t));N->data = value;N->next = NULL;//创建尾指针LNode_t *Q = L;//遍历找到链尾while(NULL != Q->next){Q = Q->next;}//链尾元素之后插入新结点Q->next = N;Q = Q->next;
}
- 4.3 指定位置插入
功能:将新结点插入到指定位置
参数:单链表的头指针、新结点的位置、新结点数据域的值
返回值:成功返回0;失败返回-1
int PosInsert_Linklist(linklist_t *L, int pos, int value){//如果第一个有效结点算作第 0 位则pos不用改动,如果第一个有效结点算作 1 位,则pos = pos - 1;//寻找插入位置的前一个结点LNode_t *Q = PosFind_Linklist(L, pos);//创建结点LNode_t *N = (LNode_t *)malloc(sizeof(LNode_t));if(NULL == N){printf("Space request error\n");return -1;}N->data = value;N->next = NULL;//插入新结点N->next = Q->next;Q->next = N;return 0;
}
如果要是一次性成型的话可以将链表的创建和插入写在一起,如果需要后续进行增删改建议还是分开写。然后设置 mode_flag 进行快速插入、删除或者单个插入、删除。
五、链表的删除操作
- 5.1 链表的头删法
int HeadDelete_Linklist(linklist_t *L){//判断参数是否正确if(NULL == L){printf("Parameter error\n");return -1;}if(0 == Length_Linklist(L)){ // Length_Linklist() 函数在上文“3.1 单链表中元素的个数”中printf("linklist is empty\n");return -1;}//头删int value = 0;LNode_t *N = L->next;L->next = N->next;N->next = NULL;value = N->data;free(N);N = NULL;return value;
}
- 5.2 链表的尾删法
int TailDelete_Linklist(linklist_t *L){//判断参数是否正确if(NULL == L){printf("Parameter error\n");return -1;}if(0 == Length_Linklist(L)){ /// Length_Linklist() 函数在上文“3.1 单链表中元素的个数”中printf("linklist is empty\n");return -1;}//每次都要遍历到尾部结点的前一个结点int i = 0;int num = Length_Linklist(L); LNode_t *N = L;while(i != num-1){i++;N = N->next;}//尾删int value = 0;value = N->next->data;free(N->next);N = NULL;return value;
}
- 5.3 任意位置删除
int PosDelete_Linklist(linklist *L, int pos){//如果第一个有效结点算作第 0 位则pos不用改动,如果第一个有效结点算作 1 位,则pos = pos - 1;//判断参数是否正确int num = Length_Linklist(L); // Length_Linklist() 函数在上文“3.1 单链表中元素的个数”中if((pos < 0) || (pos >= num)){printf("Parameter error\n");return -1;}//遍历找到 pos 位置的前一个结点LNode_t *N = PosFind_Linklist(L, pos);//删除int value = 0;LNode_t *P = N->next;value = P->data;N->next = p->next;P->next = NULL;free(P);P = NULL;return value;
}
六、链表的逆置
归根结底就是将单链表头删/尾删的方式出单链表表,然后再头插/尾插的方式入另一个单链表。
当然一个单链表也是可以实现链表的逆置的,逆置的时候第1个结点要到表尾,可以设置一个指针一直指向这个结点,对该结点之后的结点进行删除然后头插,直到后续无结点逆置完成。
- 6.1 单一链表
int Reverse_Linklist(linklist_t *L){if(NULL == L){printf("Parameter error\n");return -1;}if(NULL == L->next){printf("linklist is empty\n");return 0;}LNode_t *N = L->next;LNode_t *Q = N;while(NULL != N->next){Q = N->next;N->next = Q->next;Q->next = L->next;L->next = Q;}return 0;
}
- 6.2 两个链表
int Reverse_Linklist(linklist **L){if(NULL == L || NULL == *L){printf("Parameter error\n");return -1;}if(NULL == (*L)->next){printf("linklist is empty\n");return 0;}LNode_t *N = L;linklist *Q = NULL;while(NULL != L->next){N = L->next;L->next = N->next;N->next = Q->next;Q->next = N;}(*L) = Q;Q = NULL;return 0;
}
七、链表的置空
简单来说就是将单链表除头结点之外的结点全部删除,其实现方式无非就是当链表不为空的时候就循环头删结点,直到链表为空。
int SetEmpty_Linklist(linklist_t *L){if(NULL == L){printf("Parameter error\n");return -1;}if(NULL == L->next){printf("Linklist is empty\n");return 0;}while(NULL != L->next){HeadDelete_Linklist(L);}return 0;
}
八、链表的销毁
有了上述的基础,离链表的销毁其实只差删除头结点和将头结点指针指向NULL。
int Free_Linklist(linklist_t **L){if(NULL == *L){printf("Parameter error\n");return -1;}if(NULL != (*L)->next){SetEmpty_Linklist(*L);}free(*L);(*L) = NULL;return 0;
}