• 数据结构-线性表顺序存储结构上的基本运算实现(详解)


    查找操作

    算法思想

    查找运算可采用顺数查找,即从第一个元素开始,依次将表中的的元素与所要查找的元素进行比较,如果相等,则查找成功。如果查找成功输出相应的提示信息,反之也给予相应的提示信息。

    算法实现

    #include<stdio.h>  
    #include<stdlib.h>  
    #define MAX 20  
    
      
    typedef struct{  
        int *elem;  
        int length;  
        int listsize;  
    }SqList;  
    void CreatList(SqList &L)  
    {//建立一个线性表  
        L.elem=(int *)malloc(MAX * sizeof(int));  //动态数组空间分配
        if(!L.elem)  //! 是“非”的意思,如果没被分配空间 就结束程序。 
            return;//exit(0)  
        L.listsize=MAX;  
        printf("输入表的长度:");  
        scanf("%d",&L.length);  
        printf("输入%d个数:",L.length);  
        for(int i=0;i<L.length;i++)  
            scanf("%d",&L.elem[i]);  
    }  
    void Traverse(SqList L){  
        //遍历  
        printf("表中数据为:");  
        for(int i=0;i<L.length;i++)  
            printf("%3d",L.elem[i]);  
        printf("
    ");  
    }  
    void LocateElem(SqList L,int e){  
        //查找  
        int i;  
        printf("输入查找的元素:");  
        scanf("%d",&e);  
        for(i=0;i<L.length;i++)  
        {  
            if(L.elem[i]==e){  
                printf("查找成功,查找元素为%d",L.elem[i]);  
                printf("
    ");  
                return;// =return 0;相当于是退出程序  
            }  
        }  
        printf("查找失败");  
        printf("
    ");  
    }  
    
    int main(void)
    {
    	SqList L;  
        CreatList(L);  
        Traverse(L);  
        LocateElem(L,1);  
       
          
    	return 0;
    } 
    

    运行演示

    算法小结

    • 首先要先创建一个线性表。
    • 第二就是对线性表进行遍历,查找后输出。
    • L.elem=(int *)malloc(MAX * sizeof(int)); 动态数组空间分配, 因为要静态的数组的空间只要定义好了之后就不能再被分配了。我们这里是数组的长度是我们输入来实现的,所以是动态的。 (MAX * sizeof(int)) 这剧代码返回的是整数型的数据,所以指针用的是 int * 定义指针 的原因是因为 malloc函数只能返回第一个字节的地址,但是这个地址是没有含义的,为什么说是没有意义的呢?因为只知道这个地址,但是后面有几个字节是不知道。
    • void CreatList(SqList &L) &代表引用,使用&后所传参数会改变,仔细观察你会发现,凡是对链表有改动的,如删除函数,增加函数,形参前面都会有&符号,那是因为调用了这个函数后,所传链表(结构体)会改变。

    插入操作

    算法思想

    查找运算可采用顺数查找,即从第一个元素开始,依次将表中的的元素与所要查找的元素进行比较,如果相等,则查找成功。如果查找成功输出相应的提示信息,反之也给予相应的提示信息。

    算法实现

    #include<stdio.h>
    #include<stdlib.h>
    #define MAX 20
    
    
    typedef struct{
    	int *elem;
    	int length;
    	int listsize;
    }SqList;
    void CreatList(SqList &L)
    {//建立一个线性表
    	L.elem=(int*)malloc(MAX *sizeof(int));
    	if(!L.elem)
    		return;//exit(0)
    	L.listsize=MAX;
    	printf("输入表的长度:");
    	scanf("%d",&L.length);
    	printf("输入%d个数:",L.length);
    	for(int i=0;i<L.length;i++)
    		scanf("%d",&L.elem[i]);
    }
    void Traverse(SqList L){
    	//遍历
    	printf("表中数据为:");
    	for(int i=0;i<L.length;i++)
    		printf("%3d",L.elem[i]);
    	printf("
    ");
    }
    
    
    void ListInsert(SqList &L)  //改变链表中元素,有&符号 
    {//插入元素及其要插入的位置  
        int i;
    	int e;  
        printf("输入要插入位置及元素
    ");  
        scanf("%d%d",&i,&e);  
        printf("在顺序线性表中第%d个位置之前插入新的元素%d。
    ",i,e);//在顺序线性表L中第i个位置之前插入新的元素e,  
        if(i<1||i>L.length+1) return; //i的合法位置为1<=i<=ListLength(L)+1  
        int *p,*q;  
        q=&(L.elem[i-1]);  
        for(p=&(L.elem[L.length-1]);p>=q;--p)*(p+1)=*p;  
            *q=e;  
        
        ++L.length;  
        return;  
    }  
    
    
    
    int main(){
     SqList L;  
        CreatList(L);  
        Traverse(L);    
        ListInsert(L);
        Traverse(L);  
        
    	return 0;
    }
    
    

    运行演示

    算法小结

    • q=&(L.elem[i-1]); 表示从链表的第i个元素开始一直到最后一个元素往后移一位.
    • p=&L.elem[L.length-1] 意思是p赋初值为链表的最后一个元素地址,p>=q表示循环知道p<q的时候结束,--p是使p指针的指向往前移一位.

    删除操作

    算法思想

    用顺序表作为线性表的存储结构时,由于结点的物理顺序必须和结点的逻辑顺序保持一致,因此当需要删除第i个元素时,必须将表中位置相似i+1,i+2,...,n-1,n上的节点,依次前移到位置i,i+1,...n-1(其中n为L的表长度)。

    算法实现

    #include<stdio.h>
    #include<stdlib.h>
    #define MAX 20
    
    
    typedef struct{
    	int *elem;
    	int length;
    	int listsize;
    }SqList;
    void CreatList(SqList &L)//对链表有改动 形参有&符号
    {//建立一个线性表
    	L.elem=(int*)malloc(MAX *sizeof(int));
    	if(!L.elem)
    		return;//exit(0)
    	L.listsize=MAX;
    	printf("输入表的长度:");
    	scanf("%d",&L.length);
    	printf("输入%d个数:",L.length);
    	for(int i=0;i<L.length;i++)
    		scanf("%d",&L.elem[i]);
    }
    void Traverse(SqList L){
    	//遍历
    	printf("表中数据为:");
    	for(int i=0;i<L.length;i++)
    		printf("%3d",L.elem[i]);
    	printf("
    ");
    }
    
    
    
    int makesureElem(SqList L,int e)//这里需要用到两个参数,首先是 链表,另外一个就是删除的元素(表中)
    {
    	int i;
    //确定要删除的元素
    	for(i=0;i<L.length;i++)
    	{
    		if(L.elem[i]==e)
    		{
    			printf("要删除的元素 位置为 %d",i+1);
    			printf("
    ");
    			return (i+1);
    		
    		}
    	}
    	
    	printf("元素不存在");
    	printf("
    ");	return 0;
    		
    }
    
    int ListDelete(SqList &L){ //对链表有改动 形参有&符号
    	//删除元素
    	int i;
    	int e;
    	printf("输入要删除的元素");
    	scanf("%d",&e);
        i=makesureElem(L,e);
    	if((i<1)||(i>L.length)) return 0;//i的合法值为1<=i<=ListLength(L)+1
    	else{
    		int* p,* q;
    		p=&(L.elem[i-1]);//取回链表中每一个元素的地址赋值给p,则指针p指向的内容就是*p
    		e=*p;//找到表中要被删除的元素“e”(也就是*p)
    		q=L.elem+L.length-1;//尾元素的位置,更多解释看说明
    		for(++p;p<=q;++p)//初始化指针并移动,更多解释看说明
               {
                   *(p-1)=*p; //被删除元素之后的元素左移
               }
              
    		--L.length;
    		printf("元素被删除");
    	}
    return 0;
    	        
    }	
    int main(){
    	SqList L;
    	CreatList(L);
    	Traverse(L);
    	ListDelete(L);
    	Traverse(L);
    	
    	return 0;
    }
    
    

    运行演示

    算法小结

    • q=L.elem+L.length-1顺序存储结构(实际上就是数组)中,l.elem表示线性表l中存储数据(数组)的基地址(起始地址)这个也就是一开始建立顺序表的原因,其实本质上就是进行了用程序模拟了c语言中的数组,之所一开始 int* elem 的原因就是 和 数组中的的定义一样,例如数组a[2] a就是 a[0]的地址,即 a == &a[0]l.length是表的长度(数据元素个数),q是指针通过上式计算后指向尾元素和数组的情况一样,例如:int a[10],*p=a;//p指向第一个元素
      p=a+1;//指向第二个元素.
      例如:p=a+10-1;指向最后一个数组元素,即a[9].

    • for(++p;p<=q;++p)p是一个被初始化过的指针,按上面代码应该指向某类型的数组,为超表达方便,数组记为x(i)。for循环首先把p从当前位置x(k)移动到x(k+1)作为初值,只要指针没到q指向的位置,就继续循环,循环每次递增一个数据。循环体将数组当前位置数据拷贝到前一个位置。总之,初始时,如果p指向x(m),q指向x(n),n应该大于m。最终运行结果是x(m)=x(m+1)=...=x(n)。

    • 删除操作中函数的返回值是int型的,当然也可以是void的型的,如果是void型的话,把return 0;写成return;就好了。

    顺序表合并

    有两个顺序表LA 和 LB,其元素均为非递减有序排列,可设两个指针i、j 分别指向表LA 和 LB 中的元素,若LA.elem[i]>LB.elem[j],则当前先将LB.elem[j]插入到表LC中,若LA.elem[i]<=LB.elem[j],则当前先将LA.elem[i]插入到表LC中,如此进行下去,其中一个表被扫描完毕,然后再将未扫描完的表中剩余的所有元素放到表LC中。其中个一个表被扫描完毕,然后再将未扫描完的表中剩余的所有元素放到表LC中。

    算法演示

    #include<stdio.h>
    #include<stdlib.h>
    #define MAX 20
    
    
    typedef struct{
    	int *elem;
    	int length;
    	int listsize;
    }SqList;
    void CreatList(SqList &L)
    {//建立一个线性表
    	L.elem=(int*)malloc(MAX *sizeof(int));
    	if(!L.elem)
    		return;//exit(0)
    	L.listsize=MAX;
    	printf("输入La表的长度:");
    	scanf("%d",&L.length);
    	printf("输入%d个数:",L.length);
    	for(int i=0;i<L.length;i++)
    		scanf("%d",&L.elem[i]);
    }
    
    
    //创建第二个表
    void CreatList2(SqList &L2)
    {
    	L2.elem=(int*)malloc(MAX *sizeof(int));
    	if(!L2.elem)
    		return;//exit(0)
    	L2.listsize=MAX;
    	printf("输入Lb表的长度:");
    	scanf("%d",&L2.length);
    	printf("输入%d个数:",L2.length);
    	for(int i=0;i<L2.length;i++)
    		scanf("%d",&L2.elem[i]);
    } 
    
    void Traverse(SqList L){
    	//遍历
    	printf("La中数据为:");
    	for(int i=0;i<L.length;i++)
    		printf("%3d",L.elem[i]);
    	printf("
    ");
    }
    
    void Traverse2(SqList L2){
    	//遍历
    	printf("Lb表中数据为:");
    	for(int i=0;i<L2.length;i++)
    		printf("%3d",L2.elem[i]);
    	printf("
    ");
    }
    
    void Traverse3(SqList L3){
    	//遍历
    	printf("组合表Lc中数据为:");
    	for(int i=0;i<L3.length;i++)
    		printf("%3d",L3.elem[i]);
    	printf("
    ");
    }
    
    
    void MergeList_Sq(SqList la,SqList  lb,SqList  &lc)//增加的是lc的操作,前面的几个例子也说名了,只要牵扯到改变表中数据
     												   //就用引用符号&  
    {
    	int* pa = la.elem; // 对顺序表中第一个元素的地址进行初始化  
    	int* pb = lb.elem;
    	lc.listsize = lc.length = la.length + lb.length; //Lc表初始化大小 
    	int* pc = lc.elem = (int*)malloc(MAX *sizeof(int)); //请求系统进行动态内存分配 
    	if (!lc.elem)
    	{
    		return;//存储分配失败 
    	}
    	int* pa_last = la.elem + la.length - 1; //定义La 顺序表中末尾元素的地址,而且在线性表中地址都是递增,且递增幅度不变 
    	int* pb_last = lb.elem + lb.length - 1; //看下面的图就清晰了 
    	
    	while (pa <= pa_last && pb <= pb_last) //其实都是比较的地址 ,还有就是注意<= 不要写成<
    	{
    		if (*pa <= *pb)//对于指针指向的内容作比较 
    		{
    			*pc++ = *pa++; // 等价于 *pc = *pa; *pa++;  *pc++;注意此处的 *pa++ 不可以写成* (++pa),说明看算法小结 
    		}
    		else
    		{
    			*pc++ = *pb++;
    		}
    	}
    	
    	while (pa <= pa_last) *pc++ = *pa++;//插入La的剩余元素 
    	while (pb <= pb_last) *pc++ = *pb++;
    	
    	
    }
    
    
    int main(){
     SqList L,L2,L3;  
        CreatList(L);  
        Traverse(L);    
        CreatList2(L2);  
        Traverse2(L2);  
        MergeList_Sq(L,L2,L3);
        Traverse3(L3);  
    	return 0;
    }
    
    

    运行演示

    算法小结

    • int* pa_last = la.elem + la.length - 1; 这段代码就是求表中最后一个元素的地址,而地址是按一定的幅度递增的,如下图

    • while (pa <= pa_last && pb <= pb_last) 这里面的符号,一定不要写错了,一开始就是因为<= 写成了< 导致程序运行失败,并没有进行排序
      就直接存到Lc中了。

    • *pc++ = *pa++; 等价于 *pc = *pa; pa++; pc++;注意此处的 pa++ 不可以写成 (++pa),因为是先把pa 赋值给 pc ,所以不能写反了,如果写反了就是先p+1 ,然后才赋值给pc,这样程序逻辑会出错。

    线性顺序表优缺点

    优点
    1.无须为表示结点间的逻辑关系而增加额外的存储空间(因为逻辑上相邻的元素其存储的物理位置也是相邻的)。
    2.可以方便的随机存取表中的任一个元素。
    缺点
    1.插入和删除运算不方便,除表尾的位置外,在表的其他位置上进行插入或者删除操作都必须移动大量的结点,其效率低。
    2.由于顺序表要求占用连续的存储空间,存储分配只能预先进行静态分配。因为当表变化较大时,难以确定合适的存储规模。若按可能达到最大长度预先分配表空间,则可能造成一波分空间长期闲置而得不到充分利用;若事先对表长估计不足,则插入操作可能使表长超过预先分配的空间而造成溢出。

    参考文献

  • 相关阅读:
    Django基础二之URL路由系统
    Django基础一之web框架的本质
    HTTP协议超级详解
    动态规划-背包问题
    java 中对象比较大小
    排序算法
    泛型
    打jar包和使用jar包
    Mongodb中Sharding集群
    linux时间同步,ntpd、ntpdate
  • 原文地址:https://www.cnblogs.com/yjlaugus/p/8624889.html
Copyright © 2020-2023  润新知