数据结构

第二章

2017-10-11  本文已影响0人  不吃鱼的猫_8e95

title: 第二章
grammar_cjkRuby: true


[TOC]

一.线性表的概念及运算方法

线性表的逻辑结构

线性表是n个数据元素的有限序列线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的。

  1. 同一性:线性表由同一数据类型组成,每一个a[i]必须属于同一数据对象
  2. 有穷性:线性表由有限个数据元素组成,表长度就是表中数据元素的个数
  3. 有序性:线性表中相邻的两个元素之间存在着序偶关系<a[i],a[i+1]>.

线性表的运算

线性表可以进行增删改查,也可以加长或者剪短线性表的长度

1. InitList(L),线性表初始化,创造一个新的线性表
2. ListLength(L),求线性表的长度,返回线性表L中数据元素的个数
3. GetElem(L,i,x),用x返回线性表中第i数据元素的值
4. LocationElem(L,x),按值查找,确定数据元素x在表中的位置
5. ListInsert(L,i,x),插入操作,在线性表L中第i个位置之前插入一个新元素x,L的长度加一。
6. LIstDelete(L,i),删除操作,删除线性表L中的第i个元素,线性表的长度减一。
7. ListEmpty(L),判断线性表是否为空,是空则返回true,不是空则返回false。
8. ClearList(L),将已知的线性表置成空表。
9. DestoryList(L),销毁线性表。


二.线性表的顺序储存

1. 顺序表

# define MAXSIZE<线性表可能达到的最大长度>
typedef int ElemType;
typedef struct{
    ElemType elem[MAXSIZE];
    int length;    //线性表长度
}seqList;
定义一个顺序表:
seqList *L;

顺序表的长度为L->length,数据元素是L->elem[1]~L->elem[length],因C语言中数组的下标是从0开始的,为了一线性表中的数据元素的位序保持一致,可不使用数组下标为0的单元,下标的取值范围是1<=i<=MAXSIZE-1


2. 顺序表的基本运算

(1). 顺序表的初始化

创造一个空表,将表长length设为0,表示表中没有数据。

调用方法为:
Init_SeqList(&L);

(2).顺序表的插入

  1. 将a(n)~a(i)按从后向前的顺序向下移动,为新元素让出位置。
  2. 将x放在空出的第i个位置。
  3. 修改表长。

(3). 顺序表的删除

  1. 将a(i+1)~a(n)依次向上移动;
  2. 将length值减1.

(4). 在顺序表中按值查找

线性表中的按值查找是指在线性表中查找第一个与给定值x相等的数据元素。

顺序表的特点

** 线性表顺序储存的特点是用物理位置上的相邻来表示数据元素之间逻辑相邻的关系,储存密度高,且能随机的存储数据;但在进行插入,删除时会移动大量数据元素,运行效率低。而且顺序表需要事先分配储存空间若N的值变化较大时,则存储规模难以确定,估计过大会导致存储空间的浪费。**

三. 线性表的链式结构

1.单链表

单链表的定义

  1. 每一个单链表都是由一个个节点组成,节点包括两部分,储存数据的数据域和储存当前节点的下一个节点的地址信息。
  2. 节点定义如下:
typedef struct node
{
    ElemType date;      //数据域
    struct node *next;  //指针域
} LNode,*LinkList;

头结点,头指针

  1. 在单链表的第一个节点之前会附加一个节点,叫做头节点,头结点的数据域可以储存标题,表长等信息,头结点的指针域储存第一个节点的首地址,头结点由头指针指向
    由于最后一个节点没有后继节点,所以他的指针域为空(NULL)用^表示。
  2. 头指针变量的定义:LinkList H;
  3. 算法中用到的指向某节点的指针变量的声明:LNode *p;
  4. 语句p=(LinkList)malloc(sizeof(LNode));表示申请一个LNode类型的储存空间的操作,并且将值赋给变量P

2.单链表的基本运算

建立单链表

单链表的建立有两种方法,头插法和尾插法

头插法

建立单链表的例子

#include<stdio.h>
#include<stdlib.h>
typedef struct student   //定义一个结构体-》抽象数据类型 
{
    int name;
    struct student *next;   
}list,*Llist;   //list指结构体名  ,  *Llist指这个结构体的指针,相当于强制类型转换中的 (list *) 
            //创建一个节点(结构体),用来保存数据 
Llist CreatList()   //创建一个单链表,名字叫做 CreatList 
{
    int num=1;
    Llist head = (list *)malloc(sizeof(list));  //定义一个头指针,并且给头指针分配储存空间 
    head->next=NULL;                            //让头指针指向为空 
    list *s;                                    //定义数据类型为list的指针*s用来做节点 
    int x;                                      
    scanf("%d",&x);                             //输入需要保存的数据 
    while(x!=-1)                                //
    {
        s = (Llist)malloc(sizeof(list));        //为新建立的节点分配空间 
        s->name = x;                            
        s->next = head->next;                   //新节点的指针域指向空
        head->next=s;                           //让头结点指向新节点
        printf("输出次数%d",num);               
        num++;
        scanf("%d",&x); 
        
        
    }
    return head;                                //所有的链表最后都要返回头指针 
    
    
}

int main()
{
    
    printf("shuru");
    CreatList();                                //调用链表 
    
    
}

尾插法

在单链表的尾部插入节点建立单链表

尾插法建立链表代码实现


2. 求表长

算法思路:设一个指针p和计数器j,初始化使p指向结点H,j=0.若p所指的结点还有后继结点,p向后移动,计数器加1,重复上述过程,直到p->next=NULL为止。

3. 查找操作

思路:从链表的第一个结点起判断当前结点是否是第k个,如果是,则返回该节点的指针,否则继续查找下一个直到结束为止。没有第k个结点时返回空。

思路:从链表的第一个结点起判断当前结点的值是否等于x,如果是,则返回该节点的指针,否则继续查找下一个直到结束为止。没有第k个结点时返回空。

4. 插入操作

设p指向单链表中的某结点,s指向待插入的新结点,将*s插入到 *p的后面,插入过程如下:
(1): s->next=p->next;
(2): p->next=s;

q=H;
while(q->next!=p)
    q=q->next;      //找*p的直接前驱
s->next=q->next;
q->next=s;          //插入

5. 删除操作

设p指向单链表中要删除的结点,首先找到*p的前驱结点 *q,然后完成删除操作,操作过程如下:

  1. q->next=p->next;
  2. free(p);
  1. 查找第i-1个结点,若存在,则继续2,若不存在则结束;
  2. 若存在第i个结点,则继续3,否则,结束;
  3. 删除第i个结点否则结束

3. 循环链表

  1. 循环链表的特点是无须增加存储量,仅对表的链接方式稍作改变,即可使得表处理更加方便灵活。①循环链表中没有NULL指针。涉及遍历操作时,其终止条件就不再是像非循环链表那样判别p或p->next是否为空,而是判别它们是否等于某一指定指针,如头指针或尾指针等。
  2. 在单链表中,从一已知结点出发,只能访问到该结点及其后续结点,无法找到该结点之前的其它结点。而在单循环链表中,从任一结点出发都可访问到表中所有结点,这一优点使某些运算在单循环链表上易于实现。

4. 双向链表

typedef struct DuLNode
{
ElemType data;
struct DuLNode *prior,*next;
}DuLNode,*DuLinkList;

带头结点的双向循环链表的基本操作
void InitList(DuLinkList L)
{ /* 产生空的双向循环链表L */
L=(DuLinkList)malloc(sizeof(DuLNode));
if(L)
L->next=L->prior=L;
else
exit(OVERFLOW);
}

静态链表

#define MAXSIZE 100; 
typedef struct
{ 
  ElemType data; 
  int cur; 
}component,SLinkList[MAXSIZE];
 

这种存储结构,仍需要预先分配一个较大的空间,但在作为线性表的插入和删除操作时不需移动元素,仅需修改指针,故仍具有链式存储结构的主要优点。

有些高级语言中没有“指针”数据类型,只能用数组来模拟线性链表的结构,
数组元素中的指针“域”存放的不是元素在内存中的真实地址,而是在数组中的位置。这样的链表
称为静态链表。而通过定义一个较大的结构体数组来作为备用结点空间(即存储池),
每个结点应至少含有两个域:data域和cursor域。

四. 顺序表和链表的比较

  1. 顺序存储表示是将数据元素存放于一个连续的存储空间中,实现顺序存取或(按下标)直接存取。它的存储效率高,存取速度快。
  2. 顺序表的空间大小一经定义,在程序整个运行期间不会发生改变,因此,不易扩充
  3. 由于在插入或删除时,为保持原有次序,平均需要移动一半(或近一半)元素,修改效率不高。

作为一般规律,若线性表需频繁查找却很少进行插入和删除操作,或其操作和“数据元素在线性表中的位置”密切相关时,宜采用顺序表作为存储结构;若线性表需频繁进行插入和删除操作时,则宜采用链表做存储结构。

#include<stdio.h>
#include<stdlib.h>
struct Lnode/*定义链表*/
{ 
    int number;
    int password;
    struct Lnode *next;
}Lnode,*p,*q,*head;
int main(void)
{
    int n;/*n个人*/
    int i;
    int m;/*初始报数上限值*/
    int j;
    printf("pleaseenterthenumberofpeoplen:");/*输入测试人的数量*/
    scanf("%d",&n);
    loop:if(n<=0||n>30)/*检验n是否满足要求,如不满足重新输入n值*/
    {   printf("\nniserorr!\n\n"); 
        printf("pleaseenterthenumberofpeopleagainn:");
        scanf("%d",&n);
        goto loop;
    }
    for(i=1;i<=n;i++)/*建立单链表*/
    {
        if(i==1)
        {   
            head=p=(struct Lnode*)malloc(sizeof(struct Lnode));
        }
        else
        {
            q=(struct Lnode*)malloc(sizeof(struct Lnode));
            p->next=q;
            p=q;
        }
        printf("pleaseenterthe%dpeople'spassword:",i);/*输入每个人所持有的密码值*/
        scanf("%d",&(p->password));
        p->number=i;
        }
    p->next=head;/*形成循环链表*/
    p=head;
    printf("pleaseenterthenumberm:");
    scanf("%d",&m);
    printf("Thepasswordis:\n");
    for(j=1;j<=n;j++)/*输出各人的编号*/
        {
        for(i=1;i<m;i++,p=p->next);
        m=p->password;
        printf("%d",p->number);
        p->number=p->next->number;/*删除报m的节点*/
        p->password=p->next->password;
        q=p->next;
        p->next=p->next->next;
        free(q);
        }
    printf("\n\n");
    system("pause");/*等待按任意键退出*/
}
/* 
    一元多多项式的加法 
    1.先创建链表,存储多项式 
    2.输出多项式 
    3.两个多项式相加 
    4.输出多项式 
*/  
  
# include <stdio.h>  
# include <malloc.h>  
  
typedef struct dxs  //多项式节点  
{  
    float coe;  //系数  
    int exp;   //指数  
    struct dxs * pNext;  //指针域  
  
}DXS, * PDXS;  
  
PDXS creat_dxs();   //创建多项式  
void traverse(PDXS pHead);   //遍历多项式链表  
PDXS add(PDXS Da, PDXS Db);  //多项式求和  
  
int main(void)  
{  
    //用链表结构,创建两个多项式  
    PDXS Da = creat_dxs();  
    traverse(Da);  
  
    PDXS Db = creat_dxs();  
    traverse(Db);  
  
    //求两个多项式的加法  
    PDXS Dj = add(Da, Db);  
    traverse(Dj);  
  
    return 0;  
}  
  
PDXS creat_dxs()  
{  
    PDXS pHead = (PDXS)malloc(sizeof(DXS)); //创建头结点  
    pHead->pNext = NULL;  //尾指针  
  
    PDXS pTail = pHead;  
    PDXS pNew = NULL;  
  
    int len;  
    float c;  
    int e;  
    int i;  
  
    printf("输入多项式的项数:len = ");  
    scanf("%d", &len);  
  
    for(i = 0; i < len; i++)  
    {  
        printf("分别输入第%d项的系数c、指数e:", i+1);  
        scanf("%f%d", &c, &e);  
          
        pNew = (PDXS)malloc(sizeof(DXS));  
  
        //多项式项,系数、指数  
        pNew->coe = c;  
        pNew->exp = e;  
        pNew->pNext = NULL;  
  
        pTail->pNext = pNew;  
        pTail = pNew;  
    }  
  
    return pHead;  
}  
  
//遍历链表  
void traverse(PDXS pHead)  
{  
    PDXS p = pHead->pNext;  //首节点  
  
    while(p != NULL)  
    {  
        printf("(%.2f %d), ", p->coe, p->exp);  
        p = p->pNext;  
    }  
    printf("\n");  
}  
  
  
//多项式相加  
PDXS add(PDXS Da, PDXS Db)  
{  
    PDXS Dj = (PDXS)malloc(sizeof(DXS));  //和的头结点  
    Dj->pNext = NULL;  
    PDXS pTail = Dj;  //和的尾节点  
  
    PDXS Dah = Da->pNext;  //指向多项式的首节点  
    PDXS Dbh = Db->pNext;  
  
    //循环遍历多项式A,B  
    while(Dah && Dbh)  
    {  
        //比较当前两节点的指数  
        //当前 A项节点指数 < B项节点指数  
        if(Dah->exp < Dbh->exp)  
        {  
            pTail->pNext = Dah;  //将此A项加入和链表中  
            pTail = Dah;  
  
            Dah = Dah->pNext;  //A多项式向后遍历  
        }  
  
        //当前 A项节点指数 < B项节点指数  
        else if(Dah->exp > Dbh->exp)  
        {  
            pTail->pNext = Dbh;  //将此B项加入和链表中  
            pTail = Dbh;  
  
            Dbh = Dbh->pNext;  // //B多项式向后遍历  
        }  
  
        //如果两节点的指数相等  
        else  
        {  
            //当前指数的系数和不为0  
            //A项中保存系数和,把此A项加入和链表中  
            if(0 != (Dah->coe + Dbh->coe))  
            {  
                Dah->coe = Dah->coe + Dbh->coe;  
                pTail->pNext = Dah;  
                pTail = Dah;          
            }  
          
            //A,B都向后遍历  
            Dah = Dah->pNext;  
            Dbh = Dbh->pNext;              
        }  
    }  
  
    //插入剩余段  
    if(Dah != NULL)  
    {  
        pTail->pNext = Dah;  
    }  
    if(Dbh != NULL)  
    {  
        pTail->pNext = Dbh;  
    }  
  
    return Dj;  
}  

上一篇 下一篇

猜你喜欢

热点阅读