【1】概念
- 单向链表:简称单链表 。当前结点可以找到后一个结点,但是后一个结点无法找到当前结点。
- 链表的头结点:放在第一个有效结点之前,其数据域一般没有意义,或者存放链表长度。
- 头结点不是链表的必要元素
- 目的:为了操作统一和方便设立的,有了头结点后,在第一个元素前插入数据和删除第一个结点,就与其他结点操作一致了。
- 链表的头指针:指向链表第一个结点的指针。
- 头指针是链表的必要元素,只有知道头指针,才能找到链表。
- 若有头结点,则头指针指向头结点。
【2】操作单链表
1、单链表的初始化函数
注意:单链表的头结点存储这单链表的有效长度,由于单链表是数据结点的时候,单链表数据域存储数据,由此,单链表的结点是头结点还是数据结点,都是其中一种,所以在定义存储单链表结点的数据体时,使用union(联合体)定义数据域
/*
4 * function: 创建一个空的单链表
5 * @param [ in]
6 * @param [out]
7 * @return 返回单链表的头节点
8 */
9 linklist *linklist_init(void){
10 linklist* head = (linklist*)malloc(sizeof(linklist));
11 if(NULL == head){
12 printf("头结点创建失败\n");
13 }
14 head->next = NULL;
15 head->data.size = 0; //链表的有效长度赋值为0
16 return head;
17 }
2、单链表的头插函数
/*
* function: 单链表的头插函数
* @param [ in]
* @param [out]
* @return
*/
void linklist_headadd(linklist* list, datatype e){
linklist* pnew_node = (linklist*)malloc(sizeof(linklist));
if(NULL == pnew_node){
printf("申请新节点失败\n");
return;
}
pnew_node->next = NULL;
pnew_node->data.element = e;
list->data.size++;
pnew_node->next = list->next;
list->next = pnew_node;
}
3、单链表的尾插函数
在进行尾插时,我们不能直接找到尾结点,每次插入都需要循环遍历找到单链表的尾结点,时间复杂度O(n)
/*
* function: 单链表中插入数据:尾插
* @param [ in]
* @param [out]
* @return
*/
void linklist_tailadd(linklist* head, datatype e){
linklist* pnew_node = (linklist*)malloc(sizeof(linklist));
if(NULL == pnew_node){
printf("申请结点失败\n");
return;
}
linklist* tail = head;
for(; tail->next != NULL; tail=tail->next);
pnew_node->next = NULL;
pnew_node->data.element = e;
tail->next = pnew_node;
tail = pnew_node;
head->data.size++;
}
4、单链表的遍历函数
当链表为空的时候,这是遍历单链表就没哟意义,遍历就是循环打印,直到遍历指针p指向空
/*
* function: 遍历单链表
linklist* pnode;
* @param [ in]
* @param [out]
* @return
*/
void linklist_traverse(linklist* head){
if(linklist_isempty(head)){
printf("单链表为空\n");
return;
}
linklist* p = head->next;
for(;p != NULL; p = p->next){
printf(":%d\n", p->data.element);
}
}
5、单链表的判空
由于在创建单链表时,创建了头结点,所以当头结点的next为NULL时,此时单链表为空。
/*
* function: 单链表判空
* @param [ in]
* @param [out]
* @return
*/
int linklist_isempty(linklist* list){
return list->next == NULL;
}
6、单链表的删除结点 头删
在删除结点时,要将头结点存储的有效数据长度减一
/*
* function: 单链表删除节点 头删
* @param [ in]
* @param [out]
* @return
*/
datatype linklist_headdel(linklist* list){
if(linklist_isempty(list)){
printf("单链表为空,不能删除结点\n");
return -1;
}
linklist* pnode = list->next; //待删除结点
int res = pnode->data.element;
list->next = pnode->next;
pnode->next = NULL;
list->data.size--; //单链表有效数据-1
free(pnode);
return res;
7、单链表的删除结点 尾删
要尾删除结点,找到尾结点是无法就删除的,我们需要找到尾结点的前驱结点,通过前驱结点对尾结点进行删除。所以我们需要两个指针,一个遍历指针遍历单链表,找到尾结点,另一个遍历指针的前驱结点,到找到尾结点时,就知道其前驱
/*
* function: 单链表删除尾结点
* @param [ in]
* @param [out]
* @return
*/
datatype linklist_taildel(linklist* head){
if(linklist_isempty(head)){
printf("单链表为空,不能删除结点\n");
return -1;
}
int res;
linklist* pretail = head;
linklist* tail = pretail->next;
for(; tail->next != NULL; tail = tail->next, pretail = pretail->next);
pretail->next = NULL;
res = tail->data.element;
head->data.size--;
free(tail);
return res;
}
8、单链表的按位置插入
找到插入位置的前驱结点,当要插入结点的位置为n时,到n-1的时后就是插入位置的前驱结点。
(前置++,先加一,然后在返回其值)
/*
* function: 按位置插入
* @param [ in]
* @param [out]
* @return
*/
void linklist_insertIndex(linklist* head, datatype e, int index){
if(index < 1 || index > head->data.size + 1){ //要添加的位置超过了单链表的有效长度加1
printf("插入位置不合法\n");
return;
}
linklist* pnew_node = (linklist*)malloc(sizeof(linklist));
pnew_node->data.element = e;
linklist* p = head; //插入位置前驱结点
while(--index){ //找到插入位置前驱结点
p = p->next;
}
pnew_node->next = p->next;
p->next = pnew_node;
head->data.size++;
}
9、单链表的按位置删除
同按位置插入逻辑相同,需要找到删除位置的前驱结点
/*
* function: 单链表删除尾结点
* @param [ in]
* @param [out]
* @return
*/
datatype linklist_taildel(linklist* head){
if(linklist_isempty(head)){
printf("单链表为空,不能删除结点\n");
return -1;
}
int res;
linklist* pretail = head;
linklist* tail = pretail->next;
for(; tail->next != NULL; tail = tail->next, pretail = pretail->next);
pretail->next = NULL;
res = tail->data.element;
head->data.size--;
free(tail);
return res;
【3】单链表总代码
1. linklist.c
#include "linklist.h"
/*
* function: 创建一个空的单链表
* @param [ in]
* @param [out]
* @return 返回单链表的头节点
*/
linklist *linklist_init(void){
linklist* head = (linklist*)malloc(sizeof(linklist));
if(NULL == head){
printf("头结点创建失败\n");
}
head->next = NULL;
head->data.size = 0; //链表的有效长度赋值为0
return head;
}
/*
* function: 单链表的头插函数
//linklist_traverse(phead);
* @param [ in]
* @param [out]
* @return
*/
void linklist_headadd(linklist* list, datatype e){
linklist* pnew_node = (linklist*)malloc(sizeof(linklist));
if(NULL == pnew_node){
printf("申请新节点失败\n");
return;
}
pnew_node->next = NULL;
pnew_node->data.element = e;
list->data.size++;
pnew_node->next = list->next;
list->next = pnew_node;
}
/*
* function: 单链表中插入数据:尾插
* @param [ in]
* @param [out]
* @return
*/
void linklist_tailadd(linklist* head, datatype e){
linklist* pnew_node = (linklist*)malloc(sizeof(linklist));
if(NULL == pnew_node){
printf("申请结点失败\n");
return;
}
linklist* tail = head;
for(; tail->next != NULL; tail=tail->next);
pnew_node->next = NULL;
pnew_node->data.element = e;
tail->next = pnew_node;
tail = pnew_node;
head->data.size++;
}
/*
* function: 遍历单链表
linklist* pnode;
* @param [ in]
* @param [out]
* @return
*/
void linklist_traverse(linklist* head){
if(linklist_isempty(head)){
printf("单链表为空\n");
return;
}
linklist* p = head->next;
for(;p != NULL; p = p->next){
printf(":%d\n", p->data.element);
}
}
/*
* function: 单链表判空
* @param [ in]
* @param [out]
* @return
*/
int linklist_isempty(linklist* list){
return list->next == NULL;
}
/*
* function: 单链表删除节点 头删
* @param [ in]
* @param [out]
* @return
*/
datatype linklist_headdel(linklist* list){
if(linklist_isempty(list)){
printf("单链表为空,不能删除结点\n");
return -1;
}
linklist* pnode = list->next; //待删除结点
int res = pnode->data.element;
list->next = pnode->next;
pnode->next = NULL;
list->data.size--; //单链表有效数据-1
free(pnode);
return res;
}
datatype linklist_taildel(linklist* head){
if(linklist_isempty(head)){
printf("单链表为空,不能删除结点\n");
return -1;
}
int res;
linklist* pretail = head;
//linklist_traverse(phead);
linklist* tail = pretail->next;
for(; tail->next != NULL; tail = tail->next, pretail = pretail->next);
pretail->next = NULL;
res = tail->data.element;
head->data.size--;
free(tail);
return res;
}
/*
* function: 按位置插入
* @param [ in]
* @param [out]
* @return
*/
void linklist_insertIndex(linklist* head, datatype e, int index){
if(index < 1 || index > head->data.size + 1){ //要添加的位置超过了单链表的有效长度加1
printf("插入位置不合法\n");
return;
}
linklist* pnew_node = (linklist*)malloc(sizeof(linklist));
pnew_node->data.element = e;
linklist* p = head; //插入位置前驱结点
while(--index){ //找到插入位置前驱结点
p = p->next;
}
pnew_node->next = p->next;
p->next = pnew_node;
head->data.size++;
}
/*
* function: 按位置删除
* @param [ in]
* @param [out]
* @return
*/
datatype linklist_Indexdel(linklist* head, int index){
if(linklist_isempty(head)){
printf("单链表为空,不能删除结点\n");
return -1;
}
if(index < 1 ||index > head->data.size) //要删除的位置超过了单链表的有效长度
{
printf("单链表插入位置不合法\n");
return -2;
}
linklist* p = head;
while(--index){
p = p->next;
}
linklist* temp = p->next;
datatype val = temp->data.element;
p->next = temp->next;
free(temp);
head->data.size--;
return val;
}
2. linklist.h
#ifndef __LINKLIST_H__ #define __LINKLIST_H__ #include <stdio.h> #include <stdlib.h> typedef int datatype; union dataNode{ int size; //头结点是存储单链表长度 datatype element; //当结点为有效结点时,存储数据 }; typedef struct node{ struct node* next; //指针域:存储下一个节点的首地址 union dataNode data; }linklist; linklist* linklist_init(); //初始化单链表 void linklist_headadd(linklist*, datatype); //单链表添加元素 void linklist_tailadd(linklist*, datatype); //尾插法 int linklist_isempty(linklist*); //单链表判空 datatype linklist_headdel(linklist*); //单链表元素的删除 datatype linklist_taildel(linklist*); //尾删 void linklist_traverse(linklist*); //单链表遍历 void linklist_insertIndex(linklist*, datatype, int); //按位置插入 datatype linklist_Indexdel(linklist*, int); //按位置删除 #endif
3. main.c
#include <stdio.h>
#include "linklist.h"
int main(int argc, const char *argv[])
{
linklist* phead = linklist_init();
//头插向单链表中添加元素
linklist_headadd(phead, 1);
linklist_headadd(phead, 2);
linklist_headadd(phead, 3);
linklist_headadd(phead, 4);
//找到尾结点
linklist_tailadd(phead, 10);
//printf("size=%d\n", phead->data.size);
//printf("val=%d\n", phead->next->data.element);
//printf("del:%d\n", linklist_headdel(phead));
//printf("del:%d\n", linklist_headdel(phead));
//printf("del:%d\n", linklist_headdel(phead));
//printf("del:%d\n", linklist_headdel(phead));
//linklist_traverse(phead);
//尾删除
//printf("tail del:%d\n",linklist_taildel(phead));
//printf("tail del:%d\n",linklist_taildel(phead));
//printf("tail del:%d\n",linklist_taildel(phead));
//按位置删除
//printf("del 3:%d\n", linklist_Indexdel(phead, 5));
//printf("del 3:%d\n", linklist_Indexdel(phead, 0));
//printf("size=%d\n", phead->data.size);
//按位置插入
linklist_insertIndex(phead, 20, 3);
linklist_insertIndex(phead, 21, 7);
//linklist_insertIndex(phead, 22, 9); //插入位置不合法
linklist_insertIndex(phead, 66, 1);
linklist_traverse(phead);
return 0;
}