数据结构之链表篇 单链表 循环链表 双向链表

1.链表

  链表是线性表的一种,由一系列节点(结点)组成,每个节点包含一个数据域和一个指向下一个节点的指针域。链表结构可以克服数组需要预先知道数据大小的缺点,而且插入和删除元素很方便,但是失去数组随机读取的优点。链表有很多种不同类型:单向链表,双向链表和循环链表。

在链表中第一个节点叫头节点(如果有头节点)头节点不存放有效信息,是为了方便链表的删除和插入操作,第一个有效节点叫首节点,最后一个节点叫尾节点。

2.单链表的操作

  链表的操作一般有创建链表,插入节点,删除节点,遍历链表。插入节点的方法有头插法和尾插法,头插法是在头部插入,尾插法是在尾部插入。

  下面以一个带头节点,采用尾插法的链表说明链表的各种操作。

复制代码
#include<stdio.h>
#include<stdlib.h>
//单链表


//节点结构体
typedef struct node
{
    int value;//数据域
    struct node*next;//指针域
}Node;

Node*createList();//创建链表并且返回头节点指针
void deleteNode(Node*head);//删除节点
void insertNode(Node*head);//插入节点
void travelList(Node*head);//遍历链表

int main()
{
    Node*head=createList();
    travelList(head);
    insertNode(head);
    travelList(head);
    deleteNode(head);
    travelList(head);
    return 0;
}
//创建链表,返回头节点指针
Node*createList()
{
    //采用尾插法
    Node*head;//头节点
    Node*tail;//尾节点
    Node*temp=NULL;
    int i,value,size;
    head=(Node*)malloc(sizeof(Node));//头节点
    head->value=0;
    head->next=NULL;
    tail=head;
    printf("输入节点个数: ");
    scanf("%d",&size);
    printf("输入各个节点的值: ");

    for(i=0;i<size;i++)
    {
        scanf("%d",&value);
        temp=(Node*)malloc(sizeof(Node));
        temp->value=value;
        tail->next=temp;//让尾节点的指针域指向新创建的节点
        tail=temp;//尾节点改为新创建的节点
        tail->next=NULL;//让尾节点的指针域为空
    }
    return head;
}
//遍历链表
void travelList(Node*head)
{
    while(head->next!=NULL)
    {
        printf("%d\n",head->next->value);
        head=head->next;
    }
}
//插入节点
void insertNode(Node*head)
{
    int value;
    int position;
    int pos=0;
    Node*pre=NULL;//用来保存要插入节点的前一个节点
    Node*newNode;
    printf("输入要插入节点的值: ");
    scanf("%d",&value);
    printf("要插入的位置: ");
    scanf("%d",&position);
    while(head!=NULL)
    {
        pos++;
        pre=head;
        head=head->next;
        if(pos==position)
        {
            newNode=(Node*)malloc(sizeof(Node));
            newNode->value=value;
            newNode->next=pre->next;
            pre->next=newNode;
        }
    }
}
//删除节点
void deleteNode(Node*head)
{
    int value;
    Node*pre=head;
    Node*current=head->next;
    printf("输入要删除节点的值: ");
    scanf("%d",&value);
    while(current!=NULL)
    {
        if(current->value==value)
        {
            pre->next=current->next;
            free(current);//释放空间
            break;
        }
        pre=current;
        current=current->next;
    }
}

3.循环链表

  循环链表就是让尾节点的指针域不再是NULL,而是指向头节点从而形成一个环。循环链表与单链表的操作没有多少差别,只是判断链表是否空应该是

**  tail->next==head。**

4.双向链表

双向链表的每一个节点都有两个指针域,一个前驱指针,指向前一个节点,头节点的前驱指针为NULL,一个后继指针,指向后一个节点,尾节点的后继指针为NULL。双向链表可以从任一个节点开始访问到前后节点,不像单链表只能向前。代码如下。

复制代码
#include<stdio.h>
#include<stdlib.h>
//双向链表
typedef struct node
{
    int value;//数据域
    struct node* lNext;//前驱指针
    struct node* rNext;//后继指针
}Node;

Node*createList();//创建链表并且返回头节点指针
void deleteNode(Node*head);//删除节点
void insertNode(Node*head);//插入节点
void travelList(Node*head);//遍历链表

int main()
{

    Node*head=createList();
    travelList(head);
    insertNode(head);
    travelList(head);
    deleteNode(head);
    travelList(head);
    return 0;
}

Node*createList()
{
    Node*head,*tail,*temp;
    int num,value,i;
    head=(Node*)malloc(sizeof(Node));//头节点
    head->value=0;
    head->lNext=NULL;
    head->rNext=NULL;
    tail=head;
    printf("输入节点个数: ");
    scanf("%d",&num);
    printf("输入各个节点的值: ");
    for(i=0;i<num;i++)
    {
        scanf("%d",&value);
        temp=(Node*)malloc(sizeof(Node));
        temp->value=value;
        temp->lNext=tail;
        tail->rNext=temp;
        tail=temp;
        tail->rNext=NULL;
    }
    return head;
}


void deleteNode(Node*head)//删除节点
{

    int value;
    Node*pre;
    Node*current=head->rNext;
    printf("输入要删除节点的值: ");
    scanf("%d",&value);
    pre=head;
    while(current!=NULL)
    {
        if(current->value==value)
        {
            pre->rNext=current->rNext;//上一个节点指向下一个节点
            current->rNext->lNext=pre;//下一个节点的前驱指针指向上一个节点
            free(current);//删除该节点
        }
        pre=current;
        current=current->rNext;
    }
}

void insertNode(Node*head)//插入节点
{
    Node*pre,*temp;
    int value,pos;
    int num=0;
    printf("输入要插入的值: ");
    scanf("%d",&value);
    printf("输入要插入的位置: ");
    scanf("%d",&pos);
    while(head!=NULL)
    {
        num++;
        pre=head;//保存上一个节点
        head=head->rNext;//当前节点
        if(pos==num)
        {
            temp=(Node*)malloc(sizeof(Node));
            temp->value=value;
            temp->lNext=pre;
            temp->rNext=head;
            head->lNext=temp;
            pre->rNext=temp;
        }
    }
}

void travelList(Node*head)//遍历链表
{
    while(head->rNext!=NULL)
    {
        printf("%d\n",head->rNext->value);
        head=head->rNext;
    }
}
相关推荐
CS创新实验室3 小时前
从顺序表到动态数组:数据结构的永恒基石与现代语言的优雅封装
数据结构·算法
8Qi85 小时前
LeetCode 23. 合并 K 个升序链表 —— 小顶堆(PriorityQueue)
数据结构·算法·leetcode·链表·
QiLinkOS5 小时前
《打破“用爱发电”:一种基于 Gitee 与时间戳的开源权益分配机制探索》
c语言·数据结构·c++·科技·算法·gitee·开源
Boom_Shu7 小时前
长方形的关系
数据结构·c++·算法
Lsk_Smion8 小时前
力扣实训 _ [543].二叉树的直径 _ [23].合并K个升序列表
数据结构·算法·leetcode
ID_1800790547310 小时前
淘宝商品详情数据接口深度解析:架构、鉴权、数据结构与实战
数据结构·架构
散峰而望11 小时前
【算法练习】算法练习精选:陶陶摘苹果(基础+升级)、Music Notes、字串变换,你能AC几道?
数据结构·c++·算法·leetcode·贪心算法·github·动态规划
凤凰院凶涛QAQ11 小时前
《Java版数据结构 & 集合类剖析》集合框架的封装设计与顺序表:“从 Iterable 到 ArrayList:集合框架的‘职业树“
java·开发语言·数据结构
8Qi812 小时前
LeetCode 148. 排序链表 —— 解法一:自顶向下递归(分治 + 归并)
数据结构·算法·leetcode·链表·递归·分治·归并
8Qi812 小时前
LeetCode 148. 排序链表 —— 解法二:自底向上归并(迭代,O(1) 空间)
数据结构·算法·leetcode·链表·归并·迭代