链表与数组的区别以及链表相关的操作

117 阅读4分钟

1.链表与数组的区别: 谈到链表与数组的区别,可以从几个不同的角度来谈, 首先从逻辑结构

上说,两者都是数据结构的一种,但存在区别, 数组是申请的一块连续的内存空间,并且是在

编译阶段就要确定空间大小的,同时在运行阶段是不允许改变的,所以它不能够随着需要的改

变而增加或减少空间大小,所以当数据量大的时候,有可能超出了已申请好的数组上限,产生

数据越界,或者是数据量很小,对于没有使用的数组空间,造成内存浪费。 链表则是动态申请

的内存空间,并不像数组一样需要事先申请好大小,链表是现用现申请就OK,根据需求动态的

申请或删除内存空间,对于的是增加或删除数据,所以比数组要灵活。 再从物理存储即内存分

配上分析, 数组是连续的内存,对于访问数据,可以通过下标直接读取,时间复杂度为O(1),

而添加删除数据就比较麻烦,需要移动操作数所在位置后的所有数据,时间复杂度为O(N)。  

链表是物理上非连续的内存空间,对于访问数据,需要从头便利整个链表直到找到要访问的数

据,没有数组有效,但是在添加和删除数据方面,只需要知道操作位置的指针,很方便可以实

现增删,教数组比较灵活有效率。 所以综合以上,对于快速访问数据,不经常有添加删除操作

的时候选择数组实现,而对于经常添加删除数据,对于访问没有很高要求的时候选择链表。 

2.链表的创建以及链表的静态添加: 

来看看一个链表是怎么生成的以及它的静态添加代码:

#include<stdio.h>

struct Link
{
        int a;

        char c;

        struct Link *next;//在结构体中加入一个指针,为生成一个链表做准备
};

int main()
{
        struct Link t1 = {100,'a',NULL};
        								//静态给两个结构体赋值
        struct Link t2 = {200,'b',NULL};

        t1.next = &t2;//将结构体1中的指针指向结构体2,这样一个简易链表就产生了

        printf("use t1 go to input t2\n");

        printf("t1:%d %c t2:%d %c\n",t1.a,t1.c,(t1.next)->a,(t1.next)->c);

        return 0;
}

可以看到,通过结构体t1输出了t2中的值,这就是一个链表,将结构体t1和t2关联了起来 然后我们再来看看链表的遍历:

#include<stdio.h>

struct Link
{
        int a;

        char c;

        struct Link *next;
};

void printLink(struct Link *p)//不直接用t1来访问t2,而是通过指针的移动来遍历链表,从而输出两个结构体中的数据
{
        while(p != NULL){
                printf("%d %c\n",p->a,p->c);
                p = p->next;
        }
}

int main()
{
        struct Link t1 = {100,'a',NULL};

        struct Link t2 = {200,'b',NULL};

        t1.next = &t2;

        printLink(&t1);

        return 0;
}

通过这种方式也能实现对链表中数据的访问,毫无疑问,这种方式更为的简洁,尤其是当链表中元素很多的时候,采用这种遍历的方法尤为的重要。

3链表的动态添加(头插法): 静态添加显得有些呆滞,大部分时候我们还是需要动态输入数据,因为这里来介绍一种动态添加的方法即头插法.

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

struct Link
{
        int a;

        char c;

        struct Link *next;
};

void printLink(struct Link *p)//将头插法后的链表中的数据以遍历的方式打印出来
{
        while(p != NULL){
                printf("%d %c\n",p->a,p->c);
                p = p->next;
        }
}

struct Link* insertFromHead(struct Link *p)//封装的头插法函数
{
        struct Link *new;

        while(1){
                new = (struct Link*)malloc(sizeof(struct Link));

                printf("plaese input number and letter\n");

                scanf("%d",&new->a);

                getchar();//吸收scanf中的回车

 				scanf("%c",&new->c);

                if(new->a<=0){	
                        printf("You input a error data\n");
                        return p;
                }								//以用户输入小于零的数来终止头插法,大家也可以根据自己的习惯来设计

                if(p == NULL){
                        p = new;
                }else{
                        new->next = p;
                        p = new;
                }
        }
        return p;
}

int main()
{
        struct Link *head = NULL;

        head = insertFromHead(head);

        printLink(head);

        return 0;
}

可以看到当我输入0的时候,程序自动终止了,并且先输入的数据排在链表的后面,这也体现了头插法的特点,好了,关于链表的介绍就到这里了,希望能对学习链表的你们有所帮助。