数据结构[3]

138 阅读5分钟

线性表


携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

单链表上基本操作实现

  • 采用头插法建立单链表
LinkList List_HeadInsert(LinkList &L{//逆向建立单链表
LNode*s;
int x;
L=(LinkList)malloc(sizeof(LNode));//创建头结点
L->next=NULL;//初始为空链表
scanf("$d",&x);//输入结点的值
while(x!=9999){//输入9999表示结束
s=(LNode*)malloc(sizeof(LNode));//创建新结点
s->data=x;
s->next=L->next;
L->next=s;//将新结点插入表中,L为头指针scanf("%d",&x);
    }
return L;
}

这里需要注意的是malloc函数,一些知识需要在c语言书学习,大概意思就是分配一片空间出来。 采用头插法建立单链表时,读入数据的顺序与生成的链表中的元素的顺序是相反的。每个结点插入的时间为O(1),设单链表长为n,则总时间复杂度为O(n)。

  • 采用尾插法建立单链表 跟头插法类思,不再赘述

  • 按序号查找和a安置查找上一届学习过了,这里也不写了。

  • 这里要注意的是删除节点操作需要free,看下面的代码

  • p=GetElem(L,i-1);//查找删除位置的前驱结点
    q=p->next;//令g指向被删除结点p->next=g->next;//将*g结点从链中“断开”
    free(g);//释放结点的存储空间
    
    

双链表


双链表结点中有两个指针prior和next,分别指向其前驱结点和后继结点。


双链表中结点类型的描述如下:
typedef struct DNode{//定义双链表结点类型
    Elemrype data;//数据域
    struct DNode *prior,*next;
}DNode, *DLinklist;


双链表在单链表的结点中增加了一个指向其前驱的prior指针,因此双链表中的按值查找和按位查找的操作与单链表的相同。但双链表在插入和删除操做的实现上,与单链表有着大的不同。

双链表的插入操作

s->next=p->next;//将结点*s插入到结点*p之后
p->next->prior=s;
s->prior=p;
p->next=s;
//第一二两部必须在第4部之前

双链表的删除操作

p->next=g->next;
q->next->prior=p;
free(g);//释放结点空间

顺序表和链表的比较

1.存取(读写)方式 顺序表可以顺序存取,也可以随机存取,链表只能从表头顺序存取元素。例如在第i个位置上执行存或取的操作,顺序表仅需一次访问,而链表则需从表头开始依次访问i次。 2.逻辑结构与物理结构 采用顺序存储时,逻辑上相邻的元素,对应的物理存储位置也相邻。而采用链式存储时,逻辑上相邻的元素,物理存储位置不一定相邻,对应的逻辑关系是通过指针链接来表示的。 3.查找、插入和删除操作 对于按值查找,顺序表无序时,两者的时间复杂度均为O(n);顺序表有序时,可采用折半查找,此时的时间复杂度为O(log2n)。 对于按序号查找,顺序表支持随机访问,时间复杂度仅为O(1),而链表的平均时间复杂度为O(n)。顺序表的插入、删除操作,平均需要移动半个表长的元素。链表的插入、删除操作,只需修改相关结点的指针域即可。由于链表的每个结点都带有指针域,故而存储密度不够大。 4.空间分配 顺序存储在静态存储分配情形下,一旦存储空间装满就不能扩充,若再加入新元素,则会出现内存溢出,因此需要预先分配足够大的存储空间。预先分配过大,可能会导致顺序表后部大量闲置;预先分配过小,又会造成溢出。动态存储分配虽然存储空间可以扩充,但需要移动大量元素,导致操作效率降低,而且若内存中没有更大块的连续存储空间,则会导致分配失败。链式存储的结点空间只在需要时申请分配,只要内存有空间就可以分配,操作灵活、高效。

应用题

已知两个链表A和B分别表示两个集合,其元素递增排列。编制函数,求A与B的交集,并存放于A链表中。

算法思想

采用归并的思想,设置两个工作指针pa和pb,对两个链表进行归并扫描,只有同时出现在两集合中的元素才链接到结果表中且仅保留一个,其他的结点全部释放。当一个链表遍历完毕后,释放另一个表中剩下的全部结点。

LinkList Union(LinkList &la,LinkList &lb){
    pa=la->next;//设工作指针分别为pa和pb pb=lb->next;
    pc=la;//结果表中当前合并结点的前驱指针
    while(pa&&pb){
        if(pa->data==pb->data){//交集并入结果表中
            pc->next=pa;//A中结点链接到结果表
            pc=pa;
            pa=pa->next;
            u=pb;//B中结点释放
            pb=pb->next;
            free(u);
            }
        else if(pa->data<pb->data){//若A中当前结点值小于B中当前结点值
            u=pa;
            pa-pa->next;//后移指针
            free(u);//释放A中当前结点
            }
        else{//若B中当前结点值小于A中当前结点值
        u=pb;
        pb=pb->next;//后移指针
        free(u);/释放B中当前结点
        }
    }//while结束
    while(pa){//B已遍历完,A未完
        u=pa;
        pa=pa->next;
        free(u);
        while(pb){
        u=pb;
        pb=pb->next;
        free(u);
    }
        pc->next=NULL;//置结果链表尾指针为NULL 
        free(lb);//释放B表的头结点
        return la;
}

时间复杂度为o(1).