单链表基础操作

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素,链表中的数据是以结点来表示的。
单链表的最基本操作包括:增、删、查、改。
接下来是在单链表中实现的操作:
1.初始化链表:
将头结点指向空

void LinkListInit(LinkNode** head);

2.尾插一个元素到链表中:
a)若为空链表,直接将数据插入头结点
b)非空链表,则找到一个结点,在其后面进行插入

LinkNode* LinkListPushBack(LinkNode** head, LinkType value);

3.尾删链表中的一个元素:
a)若为空链表,无需删除
b)非空链表,如果只有一个结点,将其销毁并将头结点其指向空
c) 不止一个结点,则将倒数第二个结点指向空

void LinkListPopBack(LinkNode** head);

4.头插一个元素到链表中:
a)若为空链表,则直接插入到头结点上
b)非空链表,则在头结点前插入数据,且要改变头结点指向

void LinkListPushFront(LinkNode** head, LinkType value);

5.头删链表中的一个元素:
a)空链表,无法删除,直接返回
b)非空链表,直接将头结点指向下一个结点

void LinkListPopFront(LinkNode** head);

6.查找元素在链表中的位置:
通过遍历链表找到与所查找值相等的元素,若没有则返回空

LinkNode* LinkListFind(LinkNode* head, LinkType to_find);

7.在指定位置pos前插入数据:
a)若为空链表,则创建一个新的结点并插入指定数据,让头结点指向该结点
b)若头结点位置等于pos结点位置,则采用头插
c)若pos结点为空,则采用尾插,即在头结点后插入
d)直接在pos结点前插入

void LinkListInsert(LinkNode** head, LinkNode* pos, LinkType value);

8.在指定位置pos后插入数据:
a)若为空链表,则直接在头结点上进行插入
b)非空链表,则直接在pos结点后插入

void LinkListInsertAfter(LinkNode** head, LinkNode* pos, LinkType value);

9.删除指定位置pos上的元素:
a)若为空链表,直接返回
b)非空链表,若头结点与pos结点相同,则销毁头结点并将头结点指向下一个节点
c)删除pos位置上的元素,更新结点指向

void LinkListErase(LinkNode** head, LinkNode* pos);

void LinkListErase2(LinkNode** head, LinkNode** pos);

10.删除指定元素的值:
找到要删除的元素在链表中的位置,将其销毁,更新结点指向

void LinkListRemove(LinkNode** head, LinkType to_delete);

11.将指定值的所有元素都删掉:
通过循环找到所有相同的元素,销毁,并更新结点指向

void LinkListRemoveAll(LinkNode** head, LinkType value);

12.判断链表是否为空:
为空返回1,否则返回0

int LinkListEmpty(LinkNode* head);

13.求链表元素个数:
通过遍历来计算元素个数

size_t LinkListSize(LinkNode* head);

14.逆序打印链表:
采用递归的方法(或栈的方法)

void LinkListReversePrint(LinkNode* head);

代码如下:

LinkList.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

typedef char LinkType;

typedef struct LinkNode {
    LinkType data;
    struct LinkNode* next;
} LinkNode;


void LinkListInit(LinkNode** head);//初始化

LinkNode* LinkListPushBack(LinkNode** head, LinkType value);//尾插

void LinkListPopBack(LinkNode** head);//尾删

void LinkListPushFront(LinkNode** head, LinkType value);//头插

void LinkListPopFront(LinkNode** head);//头删

LinkNode* LinkListFind(LinkNode* head, LinkType to_find);//查找元素

void LinkListInsert(LinkNode** head, LinkNode* pos, LinkType value);//在指定位置前插入

void LinkListInsertAfter(LinkNode** head, LinkNode* pos, LinkType value);//在指定位置后插入

void LinkListErase(LinkNode** head, LinkNode* pos);//删除指定位置上的元素

void LinkListErase2(LinkNode** head, LinkNode** pos);

void LinkListRemove(LinkNode** head, LinkType to_delete);//删除元素

void LinkListRemoveAll(LinkNode** head, LinkType value);//删除所有指定元素

int LinkListEmpty(LinkNode* head);//判断链表是否为空

size_t LinkListSize(LinkNode* head);//求链表元素个数

void LinkListReversePrint(LinkNode* head);//逆序打印链表
#define _CRT_SECURE_NO_WARNINGS 1

#include "LinkList.h"


void LinkListInit(LinkNode** head){ //初始化链表
    if (*head == NULL){
        printf("非法,无法初始化");
        return;
    }
    *head = NULL;//让头结点为空
}

LinkNode* CreateNode(LinkType value){ //创建新结点
    LinkNode* ptr = (LinkNode*)malloc(sizeof(LinkNode));
    ptr->data = value;
    ptr->next = NULL;
    return ptr;
}

void DestroyNode(LinkNode* ptr){  //销毁结点
    free(ptr);
}

LinkNode* LinkListPushBack(LinkNode** head, LinkType value){   //尾插
    if (head == NULL){
        return NULL;//非法
    }
    if (*head == NULL){ //空链表,插在头结点上
        *head = CreateNode(value);
        return *head;
    }
    LinkNode* cur = *head;
    while (cur->next != NULL){  //非空链表,找到最后一个节点,并在其后面插入
        cur = cur->next;
    }
    cur->next = CreateNode(value);
    return cur->next;
}

void LinkListPopBack(LinkNode** head){  //尾删
    if (head == NULL){ //非法
        return;
    }
    if (*head == NULL){ //链表已为空
        printf("链表已为空\n");
        return;
    }
    LinkNode* cur = *head; //只有一个结点,将其销毁,让头结点指向空
    if (cur->next == NULL){
        DestroyNode(cur);
        *head = NULL;
        return;
    }
    while (cur->next != NULL){ //不止一个结点,找到倒数第二个结点,并指向空
        if (cur->next->next == NULL){
            LinkNode* to_delete = cur->next;
            cur->next = NULL;
            DestroyNode(to_delete);
        }
        else{
            cur = cur->next;
        }
    }
    return;
}

void LinkListPushFront(LinkNode** head, LinkType value){ //头插
    if (head == NULL){
        return;//非法
    }
    if (*head == NULL){ //链表为空,直接在头结点插入
        *head = CreateNode(value);
        return ;
    }
    LinkNode* new_node = CreateNode(value);
    new_node->next = *head;
    *head = new_node;
    return;
}

void LinkListPopFront(LinkNode** head){ //头删
    if (head == NULL){
        return;//非法
    }
    if (*head == NULL){
        printf("链表已为空\n");
        return;
    }
    LinkNode* to_delete = *head;//找到删除节点,让头结点指向下一个
    *head = (*head)->next;
    DestroyNode(to_delete);
    return;
}

LinkNode* LinkListFind(LinkNode* head, LinkType to_find){  //查找元素在链表中的位置,进行遍历
    LinkNode* cur = head;
    while (cur != NULL){
        if (cur->data == to_find){
            return cur;
        }
        cur = cur->next;    
    }
    return NULL;
}

void LinkListInsert(LinkNode** head, LinkNode* pos, LinkType value){ //在pos之前插入元素,遍历
        if (head == NULL){
        return;
        }//非法
        if (*head == NULL){  //链表为空
            *head=CreateNode(value);
            return;
        }
        if (*head == pos){  //头结点等于pos结点,采用头插
            LinkListPushFront(head, value);
            return;
        }
        if (pos == NULL){
            LinkListPushBack(head, value);
            return;
        }
        LinkNode* cur = *head;//链表中至少存在两个结点,在pos前插入新节点
        while (cur->next != NULL){
            if (cur->next == pos){
                LinkNode* new_node = CreateNode(value);
                new_node->next = pos;
                cur->next = new_node;
            }
                cur = cur->next;
        }
        return;
}

void LinkListInsertAfter(LinkNode** head, LinkNode* pos, LinkType value){
    if (head == NULL){
        return;//非法
    }
    if (*head == NULL){
        *head = CreateNode(value);
        return;
    }
    LinkNode* new_node = CreateNode(value);
    new_node->next = pos->next;
    pos->next = new_node;
    return;
}

void LinkListErase(LinkNode** head, LinkNode* pos){//删除指定位置的元素
    if (head == NULL || pos==NULL){
        return;//非法
    }
    if (*head == NULL){
        return;//空链表不需要删除
    }
    if (*head == pos){ //head结点与pos结点相同,改变head结点指向
        LinkNode* to_delete = *head;
        *head = (*head)->next;
        DestroyNode(to_delete);
        return;
    }
    LinkNode* cur = *head;//删除pos位置上的值
    while (cur->next != NULL){
        if (cur->next == pos){
            cur->next = pos->next;
            DestroyNode(pos);
            return;
        }
        cur = cur->next;
        return;
    }
}

void LinkListErase2(LinkNode** head, LinkNode** pos){//删除指定位置的元素
    if (head == NULL){
        return;//非法
    }
    if (*head == NULL){
        return;//空链表
    }
    if (*pos != NULL && (*pos)->next != NULL){
        (*pos)->data = (*pos)->next->data;
        LinkNode* to_delete = (*pos)->next;
        (*pos)->next = to_delete->next;
        DestroyNode(to_delete);
        return;
    }
    if ((*pos)->next == NULL){
        LinkNode* to_delete = *pos;
        *pos = NULL;
        DestroyNode(to_delete);
        return;
    }
}

void LinkListRemove(LinkNode** head, LinkType to_delete){// 删除指定值的元素
    if (head == NULL){
        return;//非法
    }
    if (*head == NULL){
        return;//空链表没有元素可删除
    }
    LinkNode* cur = LinkListFind(*head, to_delete);
    LinkListErase(head,cur);
}

void LinkListRemoveAll(LinkNode** head, LinkType value){//删除指定值的所有元素
    if (head == NULL){
        return;//非法
    }
    if (*head == NULL){
        return;//空链表
    }
    while (1){
        LinkNode* cur = LinkListFind(*head, value);
        if (cur == NULL){
            break;
        }
        LinkListErase(head, cur);
    }
    return;
}

int LinkListEmpty(LinkNode* head){//判定链表是否为空,为空返回1,否则返回0
    return head == NULL ? 1 : 0;
}

size_t LinkListSize(LinkNode* head){//求链表元素个数
    if (head == NULL){
        return 0;//空链表
    }
    size_t count = 0;
    LinkNode* cur = head;
    while (cur != NULL){
        ++count;
        cur = cur->next;
    }
    return count;
}

void LinkListReversePrint(LinkNode* head){ //逆序打印单链表,递归
    if (head == NULL){
        return;//非法
    }
    if (head->next!=NULL){
        LinkListReversePrint(head->next);
        printf("->[%c:%p]", head->data, head);
    }
    return;
}



#define TEST_HEADER printf("\n=========%s========\n",__FUNCTION__)

void LinkListPrint(LinkNode* head, const char* s){
    printf("%s\n", s);
    LinkNode* cur = head;
    for (; cur!=NULL; cur = cur->next){
        printf("[%c:%p]->", cur->data,cur);
    }
    printf("[NULL]\n");
    return;
}

void TestInit(){
    LinkNode* head;
    LinkListInit(&head);
    printf("head=%d,%p",head,head);
}

void TestPushBack(){
    TEST_HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head, 'a');
    LinkListPushBack(&head, 'b');
    LinkListPushBack(&head, 'c');
    LinkListPushBack(&head, 'd');
    LinkListPrint(head, "尾插四个元素");
}

void TestPopBack(){
    TEST_HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPopBack(&head);
    LinkListPrint(head, "对空链表进行删除");
    LinkListPushBack(&head, 'a');
    LinkListPopBack(&head);
    LinkListPrint(head, "删除只有一个结点的链表");
    LinkListPushBack(&head, 'a');
    LinkListPushBack(&head, 'b');
    LinkListPushBack(&head, 'c');
    LinkListPushBack(&head, 'd');
    LinkListPrint(head, "尾插四个元素");
    LinkListPopBack(&head);
    LinkListPopBack(&head);
    LinkListPrint(head, "尾删两个元素");
}

void TestPushFront(){
    TEST_HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushFront(&head, 'a');
    LinkListPushFront(&head, 'b');
    LinkListPushFront(&head, 'c');
    LinkListPushFront(&head, 'd');
    LinkListPrint(head, "头插四个元素");
}

void TestPopFront(){
    TEST_HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPopFront(&head);
    LinkListPrint(head, "删除空链表");
    LinkListPushFront(&head, 'a');
    LinkListPushFront(&head, 'b');
    LinkListPushFront(&head, 'c');
    LinkListPushFront(&head, 'd');
    LinkListPrint(head, "头插四个元素");
    LinkListPopFront(&head);
    LinkListPopFront(&head);
    LinkListPrint(head, "头删两个元素");
}

void TestFind(){
    TEST_HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head, 'a');
    LinkListPushBack(&head, 'b');
    LinkListPushBack(&head, 'c');
    LinkNode* aim_node = LinkListPushBack(&head, 'd');
    LinkListPrint(head, "尾插四个元素");
    LinkNode* pos = LinkListFind(head, 'b');
    printf("查找b,expect : %p, actual: %p\n", aim_node,pos);
    pos = LinkListFind(head, 'f');
    printf("查找f,expect : NULL, actual: %p\n", pos);
}

void TestInsert(){
    TEST_HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListInsert(&head, head, 'b');
    LinkListPrint(head, "对空链表进行插入");

    LinkListInsert(&head, head, 'a');
    LinkListPrint(head, "对只有一个结点的链表插入");

    LinkNode* pos = LinkListPushBack(&head, 'd');
    LinkListPrint(head, "尾插一个元素");
    //LinkListInsert(&head, pos, 'c');
    //LinkListPrint(head, "在结点d前插入c");
}

void TestErase(){
    TEST_HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListErase(&head, head);
    LinkListPrint(head, "删除空链表");

    LinkListPushBack(&head, 'a');
    LinkListPrint(head, "尾插一个元素");
    LinkListErase(&head, head);
    LinkListPrint(head, "删除头结点位置上的值");

    LinkListPushBack(&head, 'b');
    LinkNode* pos = LinkListPushBack(&head, 'c');
    LinkListPushBack(&head, 'd');
    LinkListPushBack(&head, 'e');
    LinkListPrint(head, "尾插四个元素");
    LinkListErase(&head, pos);
    LinkListPrint(head, "删除元素c");
}

void TestErase1(){
    TEST_HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListErase(&head, head);
    LinkListPrint(head, "删除空链表");

    LinkListPushBack(&head, 'a');
    LinkListPrint(head, "尾插一个元素");
    LinkListErase(&head, head);
    LinkListPrint(head, "删除头结点位置上的值");

    LinkListPushBack(&head, 'b');
    LinkNode* pos = LinkListPushBack(&head, 'c');
    LinkListPushBack(&head, 'd');
    LinkListPushBack(&head, 'e');
    LinkListPrint(head, "尾插四个元素");
    LinkListErase(&head, pos);
    LinkListPrint(head, "删除元素c");
}

void TestRemove(){
    TEST_HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head, 'a');
    LinkListPushBack(&head, 'b');
    LinkListPushBack(&head, 'c');
    LinkListPushBack(&head, 'd');
    LinkListPrint(head, "尾插四个元素");
    LinkListRemove(&head, 'b');
    LinkListPrint(head, "删除元素b");
    LinkListRemove(&head, 'f');
    LinkListPrint(head, "删除不存在元素f");
}

void TestRemoveall(){
    TEST_HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head, 'a');
    LinkListPushBack(&head, 'b');
    LinkListPushBack(&head, 'b');
    LinkListPushBack(&head, 'b');
    LinkListPushBack(&head, 'c');
    LinkListPushBack(&head, 'd');
    LinkListPrint(head, "尾插四个元素");
    LinkListRemoveAll(&head, 'f');
    LinkListPrint(head, "删除不存在的元素f");
    LinkListRemoveAll(&head, 'b');
    LinkListPrint(head, "删除元素b");
}

int TestEmpty(){
    TEST_HEADER;
    LinkNode* head;
    LinkListInit(&head);
    int ret = LinkListEmpty(head);
    LinkListPrint(head, "判定空链表");
    printf("expect:0, actual:%d\n", ret);
    LinkListPushBack(&head, 'a');
    LinkListPushBack(&head, 'b');
    LinkListPushBack(&head, 'c');
    LinkListPushBack(&head, 'd');
    LinkListPrint(head, "尾插四个元素");
    ret = LinkListEmpty(head);
    printf("ecpect:4, actual:%d\n", ret);
    return ret;
}

size_t TestSize(){
    TEST_HEADER;
    LinkNode* head;
    LinkListInit(&head);
    size_t ret = LinkListSize(head);
    LinkListPrint(head, "求空链表元素");
    printf("expect:0, actual:%d\n", ret);
    LinkListPushBack(&head, 'a');
    LinkListPushBack(&head, 'b');
    LinkListPushBack(&head, 'c');
    LinkListPushBack(&head, 'd');
    LinkListPushBack(&head, 'e');
    LinkListPushBack(&head, 'f');
    LinkListPrint(head, "尾插六个元素");
    ret = LinkListSize(head);
    LinkListPrint(head, "求链表元素个数");
    printf("expect:6, actual:%d\n", ret);
    return ret;
}

void TestReversePrint(){
    TEST_HEADER;
    LinkNode* head;
    LinkListInit(&head);
    LinkListPushBack(&head, 'a');
    LinkListPushBack(&head, 'b');
    LinkListPushBack(&head, 'c');
    LinkListPushBack(&head, 'd');
    LinkListPushBack(&head, 'e');
    LinkListPushBack(&head, 'f');
    LinkListPrint(head, "尾插六个元素");
    printf("逆序打印单链表\n");
    printf("[NULL]");
    LinkListReversePrint(head);
}



int main(){
    TestInit();
    TestPushBack();
    TestPopBack();
    TestPushFront();
    TestPopFront();
    TestFind();
    TestInsert();
    TestInsertAfter();
    TestErase();
    TestErase1();
    TestRemove();
    TestRemoveall();
    TestEmpty();
    TestSize();
    TestReversePrint();
    return;
}

单链表和顺序表的区别在于:
顺序表是以数组的形式存放数据;
顺序表是连续开辟一段空间以实现增、删、查、改等操作,所以大小是固定的,而单链表是每次只开辟一个结点大小的空间,用来存放当前要保存的数据并指向下一个结点,所以单链表的空间是动态变化的;
从访问元素来看,顺序表因为是数组存储,所以根据下标就可以随机访问某个元素,时间复杂度为O(1),而单链表是链式结构,想要访问某个元素只能通过遍历链表来查找,时间复杂度为O(n);
在插入、删除元素的问题上,顺序表的时间开销明显大于单链表。顺序表每次插入或删除一个元素必须将将该元素之后的所有元素整体后移或前移,时间开销很大,时间复杂度为O(n);而单链表在进行插入、删除操作时,只需要改变所插入结点的指向或所删除节点的前后结点的指向即可,时间复杂度为O(1)。
顺序表和单链表各有优缺点,在使用时要结合具体情况决定。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页