数据结构——顺序表 SqList *L 和 SqList * &L的区别

线性表是数据结构中的一种基本结构,但是在线性表的顺序存储结构的一些基本运算中,涉及到C++里的引用,对C语言的学习者可能带来一些困惑,下面就具体操作一下线性表,来实现一些基本运算,体会其用法(下都称为线性表)

基本运算:

  1. 始化线性表InitList(&L) :构造一个空的线性表 L 。
  2. 销毁线性表DestroyList(&L) :释放线性表 L 占用的内存空间。
  3. 判线性表是否为空表ListEmpty(L) :若 L 为空表,则返回真,否则返回假。
  4. 求线性表的长度ListLength(L) :返回 L 中元素个数。
  5. 输出线性表DispList(L): 当线性表 L 不为空时,顺序显示 L 中各节点的值域。
  6. 求线性表 L 中指定位置的某个数据元素GetElem(L,i,&e) :用 e 返回L 中第 i 个元素的值。
  7. 定位查找LocateElem(L,e) :返回 L 中第一个值域与 e 相等的逻辑位序。若这样的元素不存在,则返回值为 0 。
  8. 插入数据元素 ListInsert(&L,i,e) : 在 L 的 第 i 个元素之前插入新的元素 e ,L 的长度增 1
  9. 删除数据元素 ListDelete(&L,i,&e) :删除 L 的第 i 个元素,并用 e 返回其值,L 的长度减 1

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct{        //创建一个顺序表 
     int data[100];    //放元素 
     int length;       //线性表长度
 } SqList;

void InitList(SqList *&L) // 初始化顺序表 ,引用型指针 
{  
	L=(SqList *)malloc(sizeof(SqList)); //分配存放线性表的空间 
	L->length=0; 
}   //本算法的时间复杂度为 O(1) 。 顺序表 L

void DestroyList(SqList *&L)  //销毁线性表
{    	free(L);      }  

bool ListEmpty(SqList *L) //判断线性表是否为空 
{    return(L->length==0); } 

int ListLength(SqList *L)//求线性表的长度
{    return(L->length);   } 

void DispList(SqList *L)  //输出线性表
{ int i;
  if (ListEmpty(L))
      return; 
  for (i=0;i<L->length;i++)
      printf("%d ",L->data[i]);
  printf("\n");
}

void CreateList(SqList *&L,int a[],int n) //建立顺序表 
{
	int i;    
 	L=(SqList *)malloc(sizeof(SqList));  //L为SqListd的结构体类型的指针 , 就是一个线性表.
 	for (i=0;i<n;i++)  
    	L->data[i]=a[i];    //把数组a中的值都放入线性表L的data数组中
 	L->length=n; 
}

bool GetElem(SqList *L,int i,int &e)  //得到顺序表中某位置元素的值。&e 就是引用 
{ 
    if (i<1 || i>L->length)  
        return false;     // i不在范围内
    e=L->data[i-1];       //e就改变了, 
    return true;  
} // 本算法的时间复杂度为 O(1) 

int LocateElem(SqList *L, int e)  //输出线性表中第一个与e相等的元素的位置
  { int i=0; 
   while (i<L->length && L->data[i]!=e) 
       i++; 
   if (i>=L->length)  
       return 0; 
   else  
       return i+1; 
  }

bool ListInsert(SqList *&L,int i,int  e) // 向线性表中插入元素
{  int j;   
 if (i<1 || i>L->length+1) 
     return false; //参数错误时返回false   
 i--; //将顺序表逻辑序号转化为物理序号 
 for (j=L->length;j>i;j--) //将data[i..n]元素后移 一个位置
     L->data[j]=L->data[j-1];  
 L->data[i]=e; //插入元素e   
 L->length++; //顺序表长度增1   
 return true; //成功插入返回true 
}

bool ListDelete(SqList *&L,int i,int &e) //删除元素
{   int j;
	if(i<1||i>L->length) //参数错误时返回false 
     	return false;   
 	i--; //将顺序表逻辑序号转化为物理序号   
 	e=L->data[i];   
 	for (j=i;j<L->length-1;j++)  //将data[i..n-1]元素前移 
     	L->data[j]=L->data[j+1];  
 	L->length--;  //顺序表长度减1   
 	return true;  //成功删除返回true 
}//平均时间复杂度为 O(n) 。


int main(){
	int b[11]={1,3,4,6,3,15,6,35,61,21,19};
	int e=12;
	SqList *L;
	// InitList(L) //初始化线性表,分配内存空间 
	//初始化的功能合并到创建线性表中了。
  	CreateList(L,b,11);
  	DispList(L);//输出线性表
	printf("\n");
	
	GetElem(L,3,e); //得到线性表第三个元素的值,放在e中 
	printf("线性表第三个元素的值 e = %d\n",e);//看e是否改变 
	printf("\n");
	
	e=6;
	int locate = LocateElem(L,e);//看看数字第一个6 在线性表的哪个位置。
	printf("第一个6 在线性表 locate = %d\n",locate);
	printf("\n");
	
	ListDelete(L,8,e); //删去线性表第8个数字,删去的数字在e中存放。
	printf("删去的数字 e = %d\n",e);
	printf("\n");
	DispList(L);//输出线性表 
	
}

运行结果为:
在这里插入图片描述

我们可以看到,在定义时,有许多类似于:

bool GetElem(SqList *L,int i,int &e)
bool ListDelete(SqList *&L,int i,int &e) 
//这里我们看到的 *&L和 *L, 还有之前C语言中 int *a 和这里 int &a下面我们看这个


int * a 和 int &a:

int a; int &ra=a; //定义引用ra,它是变量a的引用,即别名

说明:

(1)&在此不是求地址运算,而是起标识作用。

(2)类型标识符是指目标变量的类型。

(3)声明引用时,必须同时对其进行初始化。

(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。

ra=1; 等价于 a=1;

(5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与&a相等。

(6)不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。

#include<stdio.h>
void Fun1(int &e)
{
e=9;
}

void Fun2(int *e)
{
*e=9;
}

int main()
{
int a = 5;
Fun1(a);
printf("%d\n",a);
a=5;
Fun2(&a);
printf("%d\n",a);
return 0;
}

这两个函数的最后效果是一样的,都改变了a的值。但是在函数中,参数表示的意思是不一样的。
第一个函数当中,e是一个整形变量;在第二个函数当中e是一个整形指针变量

所以有一下两点:

  • 传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。

  • 使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的 形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

更多相关应用的知识请看C++基础——引用的学习



SqList * L 和 SqList * &L的区别:

*L是指针,全称是指针变量,是一个用来保存内存地址的变量。在这里是一个指向顺序表,存储顺序表的地址的变量。

*&L是指针类型的引用,引用(reference)是c++对c语言的重要扩充。引用就是原变量的另外一个名称(别名),引用变量本身没有自己的实际存储空间,对引用变量的操作,就是在操作原变量。这里的 *&L 代表原指针。

这两个有着一个共同点,都指向顺序表 L ,如果在函数中修改L 的内容,都影响到 L 的内容。

不同点则是,在函数中修改指针本身所指向的地址,* L 不会发生改变,而 * &L会发生改变。

若要改变形参中的内容并且使用它则需要用引用,如果不需要改变子函数体中形参旳值,则不需要用引用。

首先,* &L是引用类型的指针,代表的是原指针,我们在函数中对指针的操作,都是直接对原指针的操作,无论是指针的内容,还是指针指向的地址,都会发生改变。

那么,*L为什么在函数中会改变不了所指向的地址呢?

其实,这里我们要延伸到函数形式参数和实际参数的很基础,也很重要的知识点了。

形参出现在函数定义中,在整个函数体内都可以使用。实参出现在主调函数中,进入被调函数后,实参也不能使用。在函数调用的时候,主函数把实参的值传送给被调函数的形参,从而实现数据的传送。

但是,在这个函数调用的过程中,数据传送是单向的,即数据只能由实参传到形参,而形参不会传回实参。也就是说,我们在函数中改变形参的值,实参的值是不会发生改变的,这就是函数调用中的单向值传递。

那么,回到我们的 *L 来,*L其实就是一个变量,在这里是一个形式参数。形式参数在函数中其实是对实参的拷贝,也就是说,函数中形参其实是另一个变量,一个复制原变量的新变量,有不同于原变量的内存空间,存在于函数中,函数调用结束,即刻释放内存空间。

也就是说,我们在函数中改变 *L 所指向的地址,不是在对原变量进行改变,而是对原变量的一个复制体进行改变,改变了复制体,却没有改变本体。

所以,在函数中 *L 不能改变所指向的地址。

发布了50 篇原创文章 · 获赞 50 · 访问量 2751

猜你喜欢

转载自blog.csdn.net/weixin_45691686/article/details/104641950
l