欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

线性表之单向链表

程序员文章站 2022-06-06 13:38:21
...

链表是物理存储结构上非连续,非顺序的存储结构,但是在逻辑上它是通过链表指针指向而实现的顺序结构

链表与数组的区别

· 数组是静态分布内存,链表是动态分布内存
· 数组在内存中是连续的,链表是不连续的
· 数组利用下表定位,查找的时间复杂度是O(1),链表通过遍历定位元素,查找的复杂度是O(n)
· 数组的插入和移除都需要移动其它元素,时间复杂度是O(n),;链表的插入或删除不需要移动其它元素,时间复杂度是O(1)

数组的优点

· 随机访问性比较强
· 查找速度快

数组的缺点

· 插入和删除的效率低,需要移动其它元素
· 会造成内存的浪费,因为内存是连续的,所以在申请数组的时候就必须规定内存的大小,如果长度不合适就会照成内存的浪费
· 内存空间的要求高,创建一个数组,必须要有足够的连续内存空间
· 数组的大小是固定的,在创建数组的时候就已经规定好,不能够动态拓展

链表的优点

· 插入和删除的效率高,只需要改变指针的指向就可以进行插入和删除
· 内存利用效率高,不会浪费内存,可以使用内存中细小的不连续空间,只有在有需要的时候才去创建空间,大小不固定,拓展很灵活

链表的缺点

· 查找效率低,因为链表是从第一个节点向后遍历查找

图表演示

线性表之单向链表

结构体构建单向链表

//结构体定义链表节点
typedef struct Listnode{
    int data;       //数据域
    ListNode *next; //后继指针
};

尾插法构建单向链表

//尾插法构建单向链表
struct Listnode* Creat_Listnode(){
    struct Listnode *head,*p,*q;
    head = NULL;
    int x;
    while(scanf("%d",&x) && x>0){
        p = (struct Listnode *)malloc(sizeof(struct Listnode));
        p->data = x;
        p->next = NULL;
        if(head == NULL)
            head = p;
        else
            q->next = p;
        q = p;
    }
    return head;
}

获取链表长度

//获取链表长度
int Length_Listnode(struct Listnode *head){
    int len = 0;
    struct Listnode *t;
    t = head;
    while(t != NULL){
        len++;
        t = t->next;
    }
    return len;
}

链表排序

//链表排序
void Sort_Listnode(struct Listnode *head){
    int len = Length_Listnode(head);
    if (len == 0)
        return;
    struct Listnode *p = head;
    int tmp;
    for (int i = 0; i<len-1; i++){         //冒泡排序的思想
        p = head;
        for (int j = 0; j < len-i-1; j++){
            if (p->data > p->next->data){
                tmp = p->data;
                p->data = p->next->data;
                p->next->data = tmp;
            }
            p = p->next;
        }
    }
}

链表插入节点

·主要头节点与尾节点

//顺序插入节点
struct Listnode* Insert_Listnode(struct Listnode *head, int x){
    struct Listnode *t,*p;
    t = head;
    p = (struct Listnode*)malloc(sizeof(struct Listnode));
    if(x < head->data){         //插入头节点
        p->data = head->data;
        p->next = head->next;
        head->data = x;
        head->next = p;
    }
    else{                   //非头节点插入
        while(t!=NULL){
            if(t->next==NULL || t->next->data >x){
                p->data=x;
                p->next=t->next;        //新增指针的后继指针指向当前后继指针指向的结点
                t->next=p;              //当前指针的后继指针指向当前指针
                break;                  //注意是否要跳出,跳出只删除符合的第一个数吗,不跳则删除所有符合的数
            }
            t=t->next;
        }
    }
    return head;
}

链表删除

·同样是要注意头和尾滴

//删除链表节点
struct Listnode* Delete_Listnode(struct Listnode *head, int x){
    struct Listnode *t,*p;
    t=head;
    if(head->data == x){       //头节点删除
        p = head->next;
        head->data = head->next->data;
        head->next = head->next->next;
        free(p);                //free()释放内存空间
    }
    else{                       //非头节点删除
        while(t != NULL){
            if(t->next->data == x){
                p = t->next;
                t->next = p->next;
               free(p);
                break;
            }
            t = t->next;
        }
    }
    return head;
}

链表打印

//打印链表
void Print_Listnode(struct Listnode *head){
    Listnode *t;
    t = head;
    if(t == NULL)       //判断链表为空
        printf("!链表为空!");
    else{               //顺序打印链表
        while(t != NULL){
            printf("%d ",t->data);
            t = t->next;
        }
    }
}

以上就是涉及到链表的所有操作,包括创建、排序、插入、删除、打印等,了解链表的思想,主要是要注意表头,表中和表位,以及地址的指向。emmmm,其实c++,Java应该是用面向对象的写法来写。什么,你没有对象,new一个就好了 //滑稽

完整的代码我就不贴了,略略略(主要是比较长,我都要哭了)