数据结构(C语言版)自学笔记:线性表(一):线性表的顺序表示和实现

作为一个编程小白,最近在粗略学习完C语言后,开始学习严与吴编著的《数据结构(C语言版)》
在学习过程中,发现书上对于各个算法并没有提供完整的例程,在这里我将自己所写代码上传,作为学习笔记,希望可以跟和我一样也在学习这本数据结构的同学分享,也同样希望路过的大神能够指点一二。

第二章:线性表

算法2.1:求A&B两个线性表的并集,并存储到线性表A中

#include
#include

void UnionList(int *pa,int na,int *pb,int nb,int *pc,int *numc);//求集合函数
int ListCheck(int *pa,int na,int x);//检测线性表中是否有x
int CheckSame(int *p,int n);//检测链表中是否有重复数字

#define FALSE 0;
#define TRUE 1;

int main()
{
	int i;
	int *pa,*pb,*pc,*numc;
	int na,nb,nc;

	printf("how many numbers does LA have? \n");
	scanf("%d",&na);//输入A线性表的大小
	printf("how many numbers does LB have? \n");
	scanf("%d",&nb);//输入B线性表的大小
	nc=na+nb;//暂定C的大小为A+B的和
	numc=&nc;//用numc指向nc

	pa=(int *)malloc(na*sizeof(int));
	pb=(int *)malloc(nb*sizeof(int));
	pc=(int *)malloc(nc*sizeof(int));
	//分配A、B、C线性表的内存(C线性表未必有na+nb个元素,但为了保证空间足够,直接分配na+nb大小)

	do
	{
	printf("please input A array number:\n");
	for(i=0;i<na;i++)
		scanf("%d",pa+i);
	}while(!CheckSame(pa,na));
	//给A线性表赋值,若存在相同数字,则重新输入

	do
	{
	printf("please input B array number:\n");
	for(i=0;i<nb;i++)
		scanf("%d",pb+i);
	}while(!CheckSame(pb,nb));
	//给A线性表赋值,若存在相同数字,则重新输入

	UnionList(pa,na,pb,nb,pc,numc);//求A、B并集,存放到C
	
	printf("the union array is :\n");//输出C线性表
	for(i=0;i<*numc;i++)
	{
		printf("%d\t",*(pc+i));
	}
	printf("\n");
	return 0;
}

void UnionList(int *pa,int na,int *pb,int nb,int *pc,int *numc)//求A、B集合,存放到C
{
	int i,j,k=0;
	for(i=0;i<na;i++,k++)
	{
		*(pc+k)=*(pa+i);
	}
	//先将A线性表中内容存放到C线性表中
	for(j=0;j<nb;j++)//遍历B线性表中的元素
	{
		if(ListCheck(pa,na,*(pb+j)))//当前B线性表的元素未在A中出现,将该元素插入到C线性表
		{
			*(pc+k)=*(pb+j);
			k++;
		}
		if(!ListCheck(pa,na,*(pb+j)))//当前B线性表的元素在A中出现,nc自减,这样可以保证nc最后的大小就是并集元素的数量
		{
			(*numc)--;//numc指向nc;
		}
	}
}

int ListCheck(int *pa,int na,int x)
{
	int i=0;
	while(i<na)
	{
		if(*(pa+i)==x)
			return 0;
		i++;
	}
	return 1;
}


int CheckSame(int *p,int n)//检测是否有重复数字
{
	int i,j;
	for(i=0;i<n;i++)
	{
		for(j=0;j<n;j++)
		{
			if(i==j)
				continue;
			if(*(p+i)==*(p+j))
			{
				printf("there are same numbers in the array.\n");
				return FALSE;
			}
		}
	}
	return TRUE;
}

在写完这段代码之后,我发现这段代码与题目要求有偏差:仅仅能完成求并集的功能,但并不是存放到A中,而是新建了一个C线性表存放并集。
按照题目要求的话,UnionList的思路应该是先循环检测B线性表中元是否与A线性表中元素重复,若重复,则检测下一个,若不重复,则将该元素放到A链表尾部。
在这里应该要注意:给A线性表分配的空间应该更改,随着B线性表中不同元素的增加,A线性表的空间应该增加。

·
·
·

算法2.2:归并A&B两个非递减线性表,并存储到C线性表中

#include
#include
#include
#define TRUE 1
#define FALSE 0

void OrderUnion(int *pa,int na,int *pb,int nb,int *pc);
int CheckOrder(int *p,int num);


int main(void)
{
	int a[10],b[10],c[20];//定义一个有限数组,最初想根据用户分配数组大小,但是在这个例子中我决定换一种方法。
	int *pa=a;
	int *pb=b;
	int *pc=c;
	char cc;
	int i,na,nb,nc;

	printf("input A array's numbers(less than 10):\n");
	do
	{
		i=0;na=0;//一定要放到里面初始化
		while((cc=getchar())!='\n'&&i<10)
		{
			if(isdigit(cc))//检测输入的是否为数字
			{
				ungetc(cc,stdin);
				scanf("%d",pa+i);
				i++;na++;
			}
		}
	}
	while(!(CheckOrder(pb,nb)));

	printf("input B array's numbers(less than 10):\n");//与A数组输入方法相同
	do
	{
		i=0;nb=0;
		while((cc=getchar())!='\n'&&i<10)
		{
			if(isdigit(cc))
			{
				ungetc(cc,stdin);
				scanf("%d",pb+i);
				i++;nb++;
			}
		}
	}
	while(!(CheckOrder(pb,nb)));
	nc=na+nb;

	OrderUnion(pa,na,pb,nb,pc);//非递减归并

	printf("the union array is:\n");//输出C线性表
	for(i=0;i<nc;i++)
	{
		printf("%d ",*(pc+i));
	}
	printf("\n");

	return 0;
}



int CheckOrder(int *p,int num)//检测用户输入的数组是否非递减,若不是非递减,则重新输入
{
	int i;
	for(i=1;i<num;i++)
	{
		if(*(p+i)<*(p+i-1))
		{
			printf("please input in order.\n");
			return FALSE;
		}
	}
	return TRUE;
}

void OrderUnion(int *pa,int na,int *pb,int nb,int *pc)
{
	int i=0,j=0,k=0;
	while(i<na&&j<nb)
	{
		if(*(pa+i)<*(pb+j))//如果a[i]
		{
			*(pc+k)=*(pa+i);
			i++;k++;
		}
		else//如果a[i]>=b[j],则向C线性表中插入B当前元素
		{
			*(pc+k)=*(pb+j);
			j++;k++;
		}
	}
	while(i<na)//如果A线性表还剩下元素,则向C线性表中插入A当前元素
	{
		*(pc+k)=*(pa+i);
		i++;k++;
	}
	while(j<nb)//如果B线性表还剩下元素,则向C线性表中插入A当前元素
	{
		*(pc+k)=*(pb+j);
		j++;k++;
	}
}

这个算法相对来说比较简单,相对于算法2.1,算法2.2并不需要检查重复等操作。

算法2.3:构造一个空的线性表L

这个算法我并没有搞清楚具体意思,我认为就是定义了一个L的SqList类型的结构体,然后线性表中的参数(基地址、表长度、大小)存在这个结构体中,以此新建线性表。

#include
#include

#define OVERFLOW -2

typedef struct{
	int *elem;//基地址
	int length;//表长
	int listsize;//大小
}SqList;

void main()
{
	SqList L;//定义SqList类型的结构体L
	printf("How many int?\n");
	scanf("%d",&L.listsize);//输入这个表的大小
	L.elem=(int *)malloc(L.listsize*sizeof(int));//分配存储空间
	if(!L.elem)
		exit(OVERFLOW);
	L.length=0;//空表长度为0
}

这里面有一个需要仔细辨析的点,就是表长度和表的大小,我认为教材中对这部分的阐述不是很清晰,表的长度是根据里面有没有内容,以及内容的数量决定的。但是表的大小,就是线性表所能容纳的最大限度。
举个例子来说,我现在分配了100个凳子,可以容纳100个人(表大小为100),但是凳子上一个人都没坐(空表,长度为0),也可以只坐30个人(表当前长度为30),但是不可以坐110个人(长度110不可以超过大小100)

算法2.4:在线性表中插入元素

#include
#include
#include

#define OVERFLOW -2
#define ERROR 0;

typedef struct{
	int *elem;
	int length;
	int size;
}SqList;//建立线性表信息的结构体

int ListInsert_Sq(SqList L,int l,int e);

void main()
{
	char c;
	int e,l;
	int i=0;
	SqList L;

	printf("the max size?\n");
	scanf("%d",&L.size);//输入最大的线性表大小
	L.elem=(int *)malloc(L.size*sizeof(int));//给基地址分配空间
	if(!L.elem) exit(OVERFLOW);//分配失败,返回OVERFLOW
	L.length=0;//线性表为空
	setbuf(stdin,NULL);
	//这步非常关键,因为下一步的起始条件是检测到回车输入则停止,若是不清空键盘的缓冲区,下一步会直接跳过,线性表无赋值,直接变成空线性表了。

	printf("please input array's number:\n");
	while((c=getchar())!='\n'&&L.length>L.size)
	{
		if(isdigit(c))//检测是否为数字
		{
			ungetc(c,stdin);//将C返还给缓冲区(我也不知道叫什么,大概就是键盘输入的临时存储的地方)
			scanf("%d",L.elem+i);
			L.length++;i++;//计数
		}
	}
	
	if(L.length>L.size)//检测输入的数字数量是否大于最大的空间
	{
		do
		{
			i=0;L.length=0;
			printf("the num should less than %d\n",L.size);
			printf("please input the array again:\n");
			while((c=getchar())!='\n')
			{
				if(isdigit(c))
				{	
					ungetc(c,stdin);
					scanf("%d",L.elem+i);
					L.length++;i++;
				}
			}
		}while(L.length>L.size);	
	}
	//其实只要用户正常输入,这步检测输入数字是否大于最大空间是没有必要的,因为无论用户输入多少个数字,线性表最大接受的数字数量也不会超过L.size,但是由于往后步骤里还有scanf函数,因此这步多输入的数字会直接赋值给下面的e甚至是l,也没有办法通过清空缓冲区的方式来阻止这个赋值,因此只能是在程序内部限制用户在这一步骤的输入情况.暂时没有发现更好的方法来避免这步的使用.

	printf("please input the number you want to insert:\n");
	scanf("%d",&e);
	fflush(stdin);//这步和setbuf(stdin,NULL)是一样的作用
	printf("please input the location you want ot insert the number:\n");
	scanf("%d",&l);

	if(!ListInsert_Sq(L,l,e))
	{
		exit(0);//如果插入位置有问题,退出程序
	}
	L.length++;//插入完成,线性表长度增加

	printf("New array is:\n");
	for(i=0;i<L.length;i++)
	{
	printf("%d ",*(L.elem+i));
	}
	printf("\n");
}

int ListInsert_Sq(SqList L,int l,int e)//L代表要插入的线性表,l是要插入的位置location,e是插入的数值
{
	int i;
	int *p,*q;
	if(l<0||l>L.length+1)
	{
		printf("location is illegal\n");
		return 0;//若插入位置错误,则返回0
	}
	if(L.length+1>L.size)//如果当前的线性表长度和线性表的大小相同(也就是说现在的线性表是满的,没有多余空间给新插入的数字)
	{
		L.elem=(int *)realloc(L.elem,(L.length+1)*sizeof(int));//重新分配内存空间,书中使用了一个newbase来新分配内存空间,但我不太清楚这样做的依据是什么,我认为realloc这个函数无论如何都可以附带L.elem的数据,因此也没有丢失数据的可能,直接用L.elem=新分配地址就可以了.
		if(!L.elem) exit(OVERFLOW);//分配失败,返回溢出
		L.size++;L.length++;//最大空间+1,长度+1
	}
	q=&L.elem[l-1];
	for(p=&L.elem[L.length-1];p>=q;p--)
	{
		*(p+1)=*p;
	}//插入位置后的全部元素右移.
	*q=e;
	return 1; 
}

刚开始写插入部分函数时,我没有利用指针去移动数据,而是右元素等于左元素这样的方法去移动的,但是多次调试之后发现有很多问题,因此我就使用了指针,虽然本质好像一样,但是指针的方式还是会更加准确一点,不然数数字会很麻烦.

算法2.5:在线性表中删除元素

#include
#include
#include

#define OVERFLOW -2
#define ERROR 0;

typedef struct{
	int *elem;
	int length;
	int size;
}SqList;

int ListDelete_Sq(SqList L,int l,int *pe);

void main()
{
	char c;
	int e,l;
	int i=0;
	SqList L;
	int *pe=&e;

	printf("the max size?\n");
	scanf("%d",&L.size);
	L.elem=(int *)malloc(L.size*sizeof(int));
	if(!L.elem) exit(OVERFLOW);
	L.length=0;
	setbuf(stdin,NULL);

	printf("please input array's number:\n");
	while((c=getchar())!='\n')
	{
		if(isdigit(c))
		{
			ungetc(c,stdin);
			scanf("%d",L.elem+i);
			L.length++;i++;
		}
	}
	if(L.length>L.size)
	{
		do
		{
			i=0;L.length=0;
			printf("the num should less than %d\n",L.size);
			printf("please input the array again:\n");
			while((c=getchar())!='\n')
			{
				if(isdigit(c))
				{	
					ungetc(c,stdin);
					scanf("%d",L.elem+i);
					L.length++;i++;
				}
			}
		}while(L.length>L.size);	
	}
	printf("please input the location you want ot delete the number:\n");
	scanf("%d",&l);



	if(!ListDelete_Sq(L,l,pe))
	{
		exit(0);
	}
	L.length--;

	printf("New array is:\n");
	for(i=0;i<L.length;i++)
	{
	printf("%d ",*(L.elem+i));
	}
	printf("\nThe deleted one is %d\n",*pe);
}

int ListDelete_Sq(SqList L,int l,int *pe)
{
	int i,e;
	int *p;
	if(l<0||l>L.length)
	{
		printf("location is illegal\n");
		return 0;
	}
	e=L.elem[l-1];
	*pe=e;
	for(p=&L.elem[l-1];p<=&L.elem[L.length-2];p++)
	{
		*p=*(p+1);
	}
	return 1; 
}

删除元素相对于插入元素要简单的多,因为删除元素不需要考虑线性表的空间是否足够用,另外与插入元素不同的就是元素右移变成了元素左移,此外要注意的一点就是,如果想在主函数中把DELETE函数中删除的数值带出来的话,需要使用指针的方式,或者使用函数返回值来完成操作,仅仅是在函数中改变实参的数值,是不会把这个改变带回主函数的.

算法2.6:在顺序表中查找特定要求元素

#include
#include
#include

typedef struct{
	int *elem;
	int size;
	int length;
}SqList;

int ListCheck(SqList x);
int ListCompare(SqList x,int e);

void main()
{
	char cc;
	SqList a;
	int i,n;

	printf("please input how many numbers in the List:\n");
	scanf("%d",&a.size);
	setbuf(stdin,NULL);

	a.elem=(int*)malloc(a.size*sizeof(int));
	if(!(a.elem)) exit(0);

	do
	{
		printf("please input the List's numbers :\n");
		i=0;a.length=0;
		while((cc=getchar())!='\n')
		{
			if(isdigit(cc))
			{
				ungetc(cc,stdin);
				scanf("%d",a.elem+i);
				i++;a.length++;
			}
		}
	}while(!ListCheck(a));

	printf("please input the number you want to search:\n");
	scanf("%d",&n);

	if(ListCompare(a,n))
	{
		printf("%d is the N0.%d number in this list\n",n,ListCompare(a,n)+1);
	}
	else
	{
		printf("%d is not in this list\n",n);
	}

}

int ListCheck(SqList x)
{
	int i,j;
	if(x.length<x.size)
	{
		printf("please input enough numbers:\n");
		return 0;
	}
	if(x.length>x.size)
	{
		printf("the numbers you input are larger than the List Size\n");
		return 0;
	}
	for(i=1;i<x.length;i++)
	{
		if(*(x.elem+i)<*(x.elem+i-1))
		{
			printf("please input in order!\n");
			return 0;
		}
	}
	for(i=0;i<x.length;i++)
	{
		for(j=0;j<x.length;j++)
		{
			if(i==j)
				continue;
			if(*(x.elem+i)==*(x.elem+j))
			{
				printf("some numbers of the list are same.\n");
				return 0;
			}
		}
	}
	return 1;
}

int ListCompare(SqList x,int e)
{
	for(int i=0;i<x.length;i++)
	{
		if(*(x.elem+i)==e)
		{
			return i;
		}
	}
	return 0;
}

这个就相对来说比较简单了,可以直接遍历顺序表来查找特定元素,书中拿这个算法举例子应该是为了说明时间复杂度的问题.

算法2.7:用线性表实现算法2.2

#include
#include
#include

typedef struct{
	int *elem;
	int size;
	int length;
}SqList;

int ListCheck(SqList x);
void MergeList(SqList a,SqList b,SqList c);

void main()
{
	char cc;
	SqList a,b,c;
	int i;

	printf("please input how many numbers in List A:\n");
	scanf("%d",&a.size);
	printf("please input how many numbers in List B:\n");
	scanf("%d",&b.size);
	setbuf(stdin,NULL);
	c.size=a.size+b.size;

	a.elem=(int*)malloc(a.size*sizeof(int));
	if(!(a.elem)) exit(0);
	b.elem=(int*)malloc(b.size*sizeof(int));
	if(!(b.elem)) exit(0);
	c.elem=(int*)malloc(c.size*sizeof(int));
	if(!(c.elem)) exit(0);

	do
	{
		printf("please input A List's numbers :\n");
		i=0;a.length=0;
		while((cc=getchar())!='\n')
		{
			if(isdigit(cc))
			{
				ungetc(cc,stdin);
				scanf("%d",a.elem+i);
				i++;a.length++;
			}
		}
	}while(!ListCheck(a));

	do
	{
		printf("please input B List's numbers :\n");
		i=0;b.length=0;
		while((cc=getchar())!='\n')
		{
			if(isdigit(cc))
			{
				ungetc(cc,stdin);
				scanf("%d",b.elem+i);
				i++;b.length++;
			}
		}
	}while(!ListCheck(b));
	c.length=a.length+b.length;

	MergeList(a,b,c);
	printf("the merged List C is:\n");

	for(i=0;i<c.length;i++)
	{
		printf("%d  ",*(c.elem+i));
	}
	printf("\n");
}


int ListCheck(SqList x)
{
	int i;
	if(x.length<x.size)
	{
		printf("please input enough numbers:\n");
		return 0;
	}
	if(x.length>x.size)
	{
		printf("the numbers you input are larger than the List Size\n");
		return 0;
	}
	for(i=1;i<x.length;i++)
	{
		if(*(x.elem+i)<*(x.elem+i-1))
		{
			printf("please input in order!\n");
			return 0;
		}
	}
	return 1;
}

void MergeList(SqList a,SqList b,SqList c)
{
	int i=0,j=0,k=0;
	while(i<a.length&&j<b.length)
	{
		if(*(a.elem+i)<*(b.elem+j))
		{
			*(c.elem+k)=*(a.elem+i);
			i++;k++;
		}
		else
		{
			*(c.elem+k)=*(b.elem+j);
			j++;k++;
		}
	}
	if(i<a.length)
	{
		*(c.elem+k)=*(a.elem+i);
		i++;k++;
	}
	if(j<b.length)
	{
		*(c.elem+k)=*(b.elem+j);
		j++;k++;
	}
}

算法2.7和算法2.2大致相同,在2.2中我是使用的数组,在2.7我使用的线性表,其实本质上是一样的,数组也是顺序表,也同样是分配连续的内存地址.

···

欢迎各位同学讨论与批评!

你可能感兴趣的:(学习笔记)