zoukankan      html  css  js  c++  java
  • 数据结构(二) 线性表-2

    1.顺序存储结构不足的解决办法

    线性表的顺序存储结构就是插入和删除时,需要移动大量元素

    问题的根源在于:相邻的两个数据元素的存储位置具有邻居关系

    插入操作,为了保持原有的数据元素的相邻关系,插入位置之后的数据元素以此往后移动。

    删除操作,所删元素留下的空隙自然需要你补,数据元素要往前移动。

    解决办法:采用链式存储结构

    2.链式存储结构定义

    2.1 几个重要概念

    链式存储结构的特点:用一组任意存储单元存储线性表的数据元素

    链式存储结构定义:每个元素包含一个数据域和一个指针域。指针域指向其直接后继元素。

    结点:一个数据域,一个指针域,指针域指向另一个节点

    链表:N个节点构成

    单链表:线性表由由节点构成并且,每个节点只包含一个指针域

    头指针:表的第一个节点的存储位置

    头节点:单链表第一个节点前附设一个节点,为头节点,头节点可以不存储任何信息,也可以存储如线性表的长度等附加信息,头节点的指针域存储第一个节点的指针

    2.2 头指针和头节点

    这两个概念容易混淆。需要加以区分

    头指针:

    1. 头指针是 链表指向第一个节点的指针,若链表由头节点,则为指向头节点的指针
    2. 头指针具有标识作用,常用头指针冠以链表的名字
    3. 无论链表是否为空,头指针均不为空,头指针是链表的必要元素

    头节点

    1. 头节点是为了操作的统一和方便设立的,放在第一个元素之前,其数据域一般无意义(也可存放链表长度)
    2. 有了头节点,对第一元素节点前的插入和删除第一个节点,其操作和其他节点统一
    3. 头节点不一定是链表的必要元素

    2.3 单链表的存储结构定义

    typedef int ElemType;/* ElemType类型根据实际情况而定,这里假设为int */
    
    typedef struct Node
    {
        ElemType data;
        struct Node *next;
    }Node;
    typedef struct Node *LinkList; 
    

    3.单链表的读取

    • GetElem

    4.单链表插入、删除

    • ListInsert

    • ListDelete

    5.单链表的创建

    • CreateListHead

    • CreateListTail

    6.单链表整表删除

    • ClearList

    7. 单链表结构和顺序存储结果优缺点

    我们分别从存储分配方式、时间性能、空间性能三方面来做对比。

    • 存储分配方式

      1.顺序存储结构用一段连续的存储单元依次存储线性表的数据元素。
      2.单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素。可以用到一些内存的边角料。

    • 时间性能

      1. 查找

      ​ 顺序存储结构0(1),单链表0(n)

      1. 插入和删除
        顺序存储结构需要平均移动表长一半的元素,时间为0(n)
        单链表在计算出某位置的指针后,插入和删除时间仅为0(1)
    • 空间性能:

      顺序存储结构需要预分配存储空间,分大了,容易造成空间浪费,分小了,容易发生溢出
      单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制。

    综上所述对比,我们得出一些经验性的结论:
    1 若线性表需要频繁查找,很少进行插入和删除操作时,宜采用顺序存储结构

    2 若需要频繁插入和删除肘,宜采用单链表结构

    3. 当线性表中的元素个数变化较大或者根本不知道有多大时,最好用单链表结构,这样可以不需要考虑
    存储空间的大小问题

    4. 如果事先知道线性表的大致长度,比如一年12个月,一周就是星期一至星期目共七天,这种用顺序存储结构效率会高很多.

    8. 完整代码

    #include "stdio.h"    
    #include "string.h"
    #include "ctype.h"      
    #include "stdlib.h"   
    #include "io.h"  
    #include "math.h"  
    #include "time.h"
    
    #define OK 1
    #define ERROR 0
    #define TRUE 1
    #define FALSE 0
    
    #define MAXSIZE 20 /* 存储空间初始分配量 */
    
    typedef int Status;/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
    typedef int ElemType;/* ElemType类型根据实际情况而定,这里假设为int */
    
    
    Status visit(ElemType c)
    {
        printf("%d ",c);
        return OK;
    }
    
    typedef struct Node
    {
        ElemType data;
        struct Node *next;
    }Node;
    typedef struct Node *LinkList; /* 定义LinkList */
    
    /* 初始化顺序线性表 */
    Status InitList(LinkList *L) 
    { 
        *L=(LinkList)malloc(sizeof(Node)); /* 产生头结点,并使L指向此头结点 */
        if(!(*L)) /* 存储分配失败 */
                return ERROR;
        (*L)->next=NULL; /* 指针域为空 */
    
        return OK;
    }
    
    /* 初始条件:顺序线性表L已存在。操作结果:若L为空表,则返回TRUE,否则返回FALSE */
    Status ListEmpty(LinkList L)
    { 
        if(L->next)
                return FALSE;
        else
                return TRUE;
    }
    
    /* 初始条件:顺序线性表L已存在。操作结果:将L重置为空表 */
    Status ClearList(LinkList *L)
    { 
    	LinkList p,q;
    	p=(*L)->next;           /*  p指向第一个结点 */
    	while(p)                /*  没到表尾 */
    	{
    		q=p->next;
    		free(p);
    		p=q;
    	}
    	(*L)->next=NULL;        /* 头结点指针域为空 */
    	return OK;
    }
    
    /* 初始条件:顺序线性表L已存在。操作结果:返回L中数据元素个数 */
    int ListLength(LinkList L)
    {
        int i=0;
        LinkList p=L->next; /* p指向第一个结点 */
        while(p)                        
        {
            i++;
            p=p->next;
        }
        return i;
    }
    
    /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
    /* 操作结果:用e返回L中第i个数据元素的值 */
    Status GetElem(LinkList L,int i,ElemType *e)
    {
    	int j;
    	LinkList p;		/* 声明一结点p */
    	p = L->next;		/* 让p指向链表L的第一个结点 */
    	j = 1;		/*  j为计数器 */
    	while (p && j<i)  /* p不为空或者计数器j还没有等于i时,循环继续 */
    	{   
    		p = p->next;  /* 让p指向下一个结点 */
    		++j;
    	}
    	if ( !p || j>i ) 
    		return ERROR;  /*  第i个元素不存在 */
    	*e = p->data;   /*  取第i个元素的数据 */
    	return OK;
    }
    
    /* 初始条件:顺序线性表L已存在 */
    /* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */
    /* 若这样的数据元素不存在,则返回值为0 */
    int LocateElem(LinkList L,ElemType e)
    {
        int i=0;
        LinkList p=L->next;
        while(p)
        {
            i++;
            if(p->data==e) /* 找到这样的数据元素 */
                    return i;
            p=p->next;
        }
    
        return 0;
    }
    
    
    /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L), */
    /* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
    Status ListInsert(LinkList *L,int i,ElemType e)
    { 
    	int j;
    	LinkList p,s;
    	p = *L;   
    	j = 1;
    	while (p && j < i)     /* 寻找第i个结点 */
    	{
    		p = p->next;
    		++j;
    	} 
    	if (!p || j > i) 
    		return ERROR;   /* 第i个元素不存在 */
    	s = (LinkList)malloc(sizeof(Node));  /*  生成新结点(C语言标准函数) */
    	s->data = e;  
    	s->next = p->next;      /* 将p的后继结点赋值给s的后继  */
    	p->next = s;          /* 将s赋值给p的后继 */
    	return OK;
    }
    
    /* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
    /* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */
    Status ListDelete(LinkList *L,int i,ElemType *e) 
    { 
    	int j;
    	LinkList p,q;
    	p = *L;
    	j = 1;
    	while (p->next && j < i)	/* 遍历寻找第i个元素 */
    	{
            p = p->next;
            ++j;
    	}
    	if (!(p->next) || j > i) 
    	    return ERROR;           /* 第i个元素不存在 */
    	q = p->next;
    	p->next = q->next;			/* 将q的后继赋值给p的后继 */
    	*e = q->data;               /* 将q结点中的数据给e */
    	free(q);                    /* 让系统回收此结点,释放内存 */
    	return OK;
    }
    
    /* 初始条件:顺序线性表L已存在 */
    /* 操作结果:依次对L的每个数据元素输出 */
    Status ListTraverse(LinkList L)
    {
        LinkList p=L->next;
        while(p)
        {
            visit(p->data);
            p=p->next;
        }
        printf("
    ");
        return OK;
    }
    
    /*  随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */
    void CreateListHead(LinkList *L, int n) 
    {
    	LinkList p;
    	int i;
    	srand(time(0));                         /* 初始化随机数种子 */
    	*L = (LinkList)malloc(sizeof(Node));
    	(*L)->next = NULL;                      /*  先建立一个带头结点的单链表 */
    	for (i=0; i<n; i++) 
    	{
    		p = (LinkList)malloc(sizeof(Node)); /*  生成新结点 */
    		p->data = rand()%100+1;             /*  随机生成100以内的数字 */
    		p->next = (*L)->next;    
    		(*L)->next = p;						/*  插入到表头 */
    	}
    }
    
    /*  随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法) */
    void CreateListTail(LinkList *L, int n) 
    {
    	LinkList p,r;
    	int i;
    	srand(time(0));                      /* 初始化随机数种子 */
    	*L = (LinkList)malloc(sizeof(Node)); /* L为整个线性表 */
    	r=*L;                                /* r为指向尾部的结点 */
    	for (i=0; i<n; i++) 
    	{
    		p = (Node *)malloc(sizeof(Node)); /*  生成新结点 */
    		p->data = rand()%100+1;           /*  随机生成100以内的数字 */
    		r->next=p;                        /* 将表尾终端结点的指针指向新结点 */
    		r = p;                            /* 将当前的新结点定义为表尾终端结点 */
    	}
    	r->next = NULL;                       /* 表示当前链表结束 */
    }
    
    int main()
    {        
        LinkList L;
        ElemType e;
        Status i;
        int j,k;
        i=InitList(&L);
        printf("初始化L后:ListLength(L)=%d
    ",ListLength(L));
        for(j=1;j<=5;j++)
                i=ListInsert(&L,1,j);
        printf("在L的表头依次插入1~5后:L.data=");
        ListTraverse(L); 
    
        printf("ListLength(L)=%d 
    ",ListLength(L));
        i=ListEmpty(L);
        printf("L是否空:i=%d(1:是 0:否)
    ",i);
    
        i=ClearList(&L);
        printf("清空L后:ListLength(L)=%d
    ",ListLength(L));
        i=ListEmpty(L);
        printf("L是否空:i=%d(1:是 0:否)
    ",i);
    
        for(j=1;j<=10;j++)
                ListInsert(&L,j,j);
        printf("在L的表尾依次插入1~10后:L.data=");
        ListTraverse(L); 
    
        printf("ListLength(L)=%d 
    ",ListLength(L));
    
        ListInsert(&L,1,0);
        printf("在L的表头插入0后:L.data=");
        ListTraverse(L); 
        printf("ListLength(L)=%d 
    ",ListLength(L));
    
        GetElem(L,5,&e);
        printf("第5个元素的值为:%d
    ",e);
        for(j=3;j<=4;j++)
        {
                k=LocateElem(L,j);
                if(k)
                        printf("第%d个元素的值为%d
    ",k,j);
                else
                        printf("没有值为%d的元素
    ",j);
        }
        
    
        k=ListLength(L); /* k为表长 */
        for(j=k+1;j>=k;j--)
        {
                i=ListDelete(&L,j,&e); /* 删除第j个数据 */
                if(i==ERROR)
                        printf("删除第%d个数据失败
    ",j);
                else
                        printf("删除第%d个的元素值为:%d
    ",j,e);
        }
        printf("依次输出L的元素:");
        ListTraverse(L); 
    
        j=5;
        ListDelete(&L,j,&e); /* 删除第5个数据 */
        printf("删除第%d个的元素值为:%d
    ",j,e);
    
        printf("依次输出L的元素:");
        ListTraverse(L); 
    
        i=ClearList(&L);
        printf("
    清空L后:ListLength(L)=%d
    ",ListLength(L));
        CreateListHead(&L,20);
        printf("整体创建L的元素(头插法):");
        ListTraverse(L); 
        
        i=ClearList(&L);
        printf("
    删除L后:ListLength(L)=%d
    ",ListLength(L));
        CreateListTail(&L,20);
        printf("整体创建L的元素(尾插法):");
        ListTraverse(L); 
    
    
        return 0;
    }
    

  • 相关阅读:
    搜索回车跳转页面
    登录验证码
    【排序算法】排序算法之插入排序
    PAT 乙级 1044 火星数字 (20 分)
    PAT 甲级 1035 Password (20 分)
    PAT 甲级 1041 Be Unique (20 分)
    PAT 甲级 1054 The Dominant Color (20 分)
    PAT 甲级 1027 Colors in Mars (20 分)
    PAT 甲级 1083 List Grades (25 分)
    PAT 甲级 1005 Spell It Right (20 分)
  • 原文地址:https://www.cnblogs.com/chance0x1/p/13096013.html
Copyright © 2011-2022 走看看