远古课程作业 数据结构与算法实习 链表的基本操作 (附源码和实验报告,95分以上,做法不一定全对)

目录

链表的基本操作

链栈的基本操作及进制转换

实验报告


链表的基本操作

//链表的基本操作
#include <iostream>
using namespace std;
#include <iomanip>
#include <conio.h>
#include <stdio.h>
#include <process.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>

//定义结点类型
struct Node{
		int data;
		Node *next;
		};

Node Head;       //头结点
Node *DLList;    //头指针
Node *La,*Lb,*Lc;//生成La和Lb且合并到Lc所用到的变量 
Node Lahead,Lbhead,Lchead; 
Node Ahead;//头结点
Node Bhead; 
Node *A,*B;

void init(Node *DLList);
void display(Node *DLList);
void insert(Node *DLList);
void search(Node *DLList);
void del(Node *DLList);
void wait();

void insertSort(Node *DLList);//直接插入排序 
void reverseList(Node *DLList);//逆置
void sortedMergeListA_B(Node *La,Node *Lb,Node *Lc); //生成并且合并
void divideList(Node *A,Node *B);//划分 

int  main()
{
	int choice;

	DLList=&Head;     //使头指针指向头结点
	Head.next=NULL;
	
	A=&Ahead;
	Ahead.next=NULL;
	B=&Bhead;
	Bhead.next=NULL; 
	
	La=&Lahead;
	Lahead.next=NULL;
	Lb=&Lbhead;
	Lbhead.next=NULL;
	Lc=&Lchead;
	Lchead.next=NULL; 

	while (1)
	{
		system("cls");
		cout << "\n\n\n\n";
		cout << "\t\t              单链表操作  \n";
		cout << "\t\t======================================";
		cout << "\n\n";
		cout << "\t\t             1:初始化      \n";
		cout << "\t\t             2:显示        \n";
		cout << "\t\t             3:单个插入    \n";
		cout << "\t\t             4:查找        \n";
		cout << "\t\t             5:删除        \n";
		cout << "\t\t             6:直接插入排序        \n";
		cout << "\t\t             7:逆置链表        \n";
		cout << "\t\t             8:生成有序的两个单链表A和B(链表的数据和个数自定),并合并链表A和B为一个有序的表C        \n";
		cout << "\t\t             9:划分链表为奇数序号表和偶数序号表        \n";		
		cout << "\t\t             0:退出        \n";
		cout << "\n";
		cout << "\t\t请选择:" << flush;

		scanf( "%d",&choice );
		system("cls");
		
		switch(choice)
		{
			case 1:
				init(DLList);
				display(DLList);
				wait(); 
				break;
			case 2:
				display(DLList);
				wait();
				break;
			case 3:
				insert(DLList);
				display(DLList);
				wait(); 
				break;
			case 4:
				search(DLList);
				wait();
				break;
			case 5:
				del(DLList);
				display(DLList);
				wait(); 
				break;
			case 6:
				insertSort(DLList);
				display(DLList);
				wait();
				break;
			case 7:
				reverseList(DLList);
				display(DLList);
				wait();
				break; 
			case 8: 
				sortedMergeListA_B(La,Lb,Lc);
				display(Lc);
				DLList=Lc; // 用全局变量A的值覆盖全局变量DLList,以便对合并后的表进行显示,删除,插入等操作 
				wait();
				break;
			case 9:
				divideList(A,B); 
				DLList=A;//用全局变量A的值覆盖全局变量DLList 
				wait(); 
				break; 
			default :
					printf("您输入的数字有误!请重新输入!\n");
					wait(); 
					break;
			case 0:
				exit(0);
		}
	}
	return 0;
}

//公用的等待函数
void wait()
{
	cout << "请按任意键继续" << flush;
	getch();
}

//屏幕提示后,从键盘输入线性表长度和随机数种子,生成以DLList为头指针的指定长度的线性表
void init(Node *DLList)
{
	int length;
	Node *p,*q;
	while (1)
	{
	    cout << "输入元素个数(0-" << 10000 << "):" << flush;
		cin >> length;
		if (length >= 0 && length <= 10000)
			break;
		//如果没有break;则说明数据有误 
		printf( "输入有误,请重新输入!\n" );
		wait();
		cout << endl;
	}

    int i;
	while (1)
	{
	    cout << "输入随机数种子(0-32767):" << flush;
		cin >> i;
		if (i >= 0 && i <= 32767)
			break;
		//如果没有break;则说明数据有误 
		printf( "输入有误,请重新输入!\n" );
		wait();
		cout << endl;
	}

    //从线性表中删除并释放原有的结点,使其成为空表
	p=DLList;
	while (p->next != NULL)
	{
		q=p->next;
		p->next=q->next;
		free(q);
	}

	srand(i);  //指定随机数种子,相同的种子将产生相同的数据序列
	rand();  

	//向线性表插入length个新结点
    for (int j=1;j<=length;j++)    
    {
		p=new Node;
		p->next=DLList->next;
		DLList->next=p;
		p->data=rand() % 10000;
    }
}

//在屏幕上依次显示以DLList为头指针的线性表中的全部元素和元素个数
//格式应便于观察
//如果需要指定输出的宽度,可以使用%md ,其中 m是占据的列数
void display(Node *DLList)
{
	int i; 
	int count=0;//保存元素个数 
	Node *p,*q;
	p=DLList;//p指向头结点 
	
	while(p->next != NULL)//p->next指向含有元素的结点 
	{
		count++; //结点非空,元素个数加一 
		p=p->next;//指针后移 
	}
	
	printf( "元素个数:%d\n",count );
	printf( "Tips:为显示清楚,每行显示五个元素。\n" ); 
	printf( "全部元素:\n" );
	
	p=DLList->next;//p指向首元 结点 
	for(i=0;i<count;i++)
	{
		if(i%5 == 0)//每行显示5个 ,便于观察 
		{
			printf( "\n" );
		} 
		printf( "%-4d\t",p->data ); 
		p=p->next;
	} 
	
	printf( "\n\n" );
}

//屏幕提示后,从键盘输入一个元素值,然后把这个新元素插到以DLList为头指针的线性表的末尾
void insert(Node *DLList)
{
	printf( "请输入要插入到线性表末尾的元素值:" );
	int x;
	scanf( "%d",&x ) ;
	Node *p=DLList;//p初始化指向头结点
	Node *q;
	q=new Node;//申请空间,保存插入的元素
	q->data=x; 
	while(p->next != NULL)//循环结束后,p指向单链表最后一个结点 
	{
		p=p->next;
	}
	//位置找到了,开始插入
	p->next=q;
	q->next=NULL;
	printf( "插入成功!\n" ); 
}

//屏幕提示后,从键盘输入一个元素值,在以DLList为头指针的线性表中搜索这个元素
void search(Node *DLList)
{
	if(DLList->next == NULL)//如果为空表,则不进行查找,并会有相应提示 
	{
		printf( "单链表中没有元素!请先初始化为非空表或插入元素后再试!\n" ); 
		return ;
	} 
	
	printf( "请输入要查找的元素值:" );
	int x;
	scanf( "%d",&x );
	Node *p;
	int flag=0;//0表示未查找到,1表示查找到了 
	int count=0;//比较次数 
	
	p=DLList->next;//p初始化指向首元结点
	while(p != NULL) //进行查找 
	{
		count++; 
		if(x == p->data)//比较是否相等 
		{
			flag=1; //找到了,变为1 
			printf( "查找成功!\n" ); 
			printf( "比较次数:%d\n",count );
			break;
		}
		p=p->next;
	}
	
	if(flag == 0)
	{
		printf( "查找失败!\n" ); 
		printf( "比较次数:%d\n",count );
	}
}

//屏幕提示后,从键盘输入一个元素值,在以DLList为头指针的线性表中删除这个元素
//屏幕显示删除成功与否的信息
void del(Node *DLList)
{
	if(DLList->next == NULL)//如果为空表,则不进行删除,并会有相应提示 
	{
		printf( "单链表中没有元素!请先初始化为非空表或插入元素后再试!\n" ); 
		return ;
	}
	
	printf( "请输入要删除的元素值:" );
	int x;
	scanf( "%d",&x );
	Node *p;
	Node *q;//指向要删除的结点,以便释放空间 
	int flag=0;//0表示表未找到输入的元素值,1表示找到了输入的元素值 
	int count=0;//比较次数 
	
	p=DLList;//p初始化指向头结点
	while(p->next != NULL) //进行查找 
	{
		count++; 
		if(x == p->next->data)//与表中每一个元素比较是否相等 ,p始终指向与x比较的结点的前一个结点 
		{
			flag=1; //找到了,变为1
			//开始删除
			q=p->next;//指向要删除的结点 
			p->next=q->next;
			free(q);//释放空间 
			printf( "删除成功!\n" ); 
			printf( "比较次数:%d\n",count );
			break;
		}
		p=p->next;
	}
	
	if(flag == 0)
	{
		printf( "删除失败!\n" ); 
		printf( "比较次数:%d\n",count );
	}
	
}
 
 //直接插入排序 
 void insertSort(Node *DLList)
{
	if(DLList->next == NULL)//如果为空表,则不进行排序,并会有相应提示 
	{
		printf( "单链表中没有元素!请先初始化为非空表或插入元素后再试!\n" ); 
		return ;
	}
	if(DLList->next->next == NULL)//如果为空表,则不进行排序,并会有相应提示 
	{
		printf( "单链表中只有一个元素,已经有序!无需进行插入排序!\n" ); 
		return ;
	}
	
 	Node *p,*q,*x; 
	p=DLList->next;//p指向首结点 
	int flag;// 
	
	while(p->next != NULL)    
	{
		flag=0; 
	//p指向倒数第二个结点时,p->next就指向最后一个结点,最后一个结点就开始进行直接插入排序了 
		if(p->next->data < p->data)//要进行插入排序的结点的元素值如果大于等于它的前继,则 该结点不需要往前插入
		{	
			flag=1;						//如果小于,则需要往前找到它的位置 
			x=p->next;//将要进行插入排序的结点保存在x 
			p->next=p->next->next;// 结点要往前插入,则结点在原来位置就该被删除,以便后续仍然是单链表的一部分 
			q=DLList;//q指向头结点 			//结点的往前插入,应保证往前插入后,链表依然是单链表,不能形成循环 
			while(1)					// 在if语句之内,则x->data必然小于p->data 
			{							 
				if(x->data < q->next->data) //总会出现满足此条件的情况,因此,不是死循环 
					break;					//当q->next==p时, q->next->data必然大于x->data 
				q=q->next;	
			}//循环结束后找到了x要插入的位置,就是q的后面 
 			x->next=q->next;
			q->next=x;//插入到正确位置
		}
		if(flag == 0) //flag==1时,(即执行了上面的if语句中的p->next=p->next->next;)p不后移,这时,p ->next仍然指向下一个待排序的结点 
		p=p->next;
	}
	
	printf( "直接插入排序成功!\n");	
} 


//应用题
void reverseList(Node *DLList)//逆置 
{
	if(DLList->next == NULL)//如果为空表,则不进行逆置,并会有相应提示 
	{
		printf( "单链表中没有元素!请先初始化为非空表或插入元素后再试!\n" ); 
		return ;
	}
	if(DLList->next->next == NULL)//如果只有一个元素,则不进行逆置,并会有相应提示 
	{
		printf( "单链表中只有一个元素,逆置与否都一样!\n" ); 
		return ;
	}
	
	Node *p,*q;
	p=DLList->next->next;//p指向第二个元素所在的结点
	DLList->next->next=NULL;//首元结点的指针域置空,相当于把它当作最后一个结点了
	 
	while(p != NULL)//从第二个元素结点开始,往前插入到头结点和首结点之间 
	{
		q=p;		//p操控遍历,q作为往前插的媒介 
		p=p->next;	//p后移一个位置,q指向原来p所指向的结点 
		q->next=DLList->next;//将q指向的结点往前插入 
		DLList->next=q; 
	}
	
	printf("逆置成功!\n"); 
} 

//生成有序的两个单链表A和B(链表的数据和个数自定),其首结点指针分别为a和b,要求将两
//个单链表合并为一个有序的单链表C,其首结点指针为c,并且合并后的单链表的数据不重复。
void sortedMergeListA_B(Node *La,Node *Lb,Node *Lc)
{
	Node *p,*q,*r;
	int i;
	int A_length,B_length;
	
	//生成表A 
	while(1)
	{
		printf( "请输入链表A的元素个数(>0):" );
		scanf( "%d",&A_length );//自定个数
		if(A_length>0)
			break;
		printf( "输入有误,请重新输入!\n" );
		wait();	
		printf( "\n" );
	}

	//向线性表插入length个新结点 
	for(i=0;i<A_length;i++)		//自定数据 
	{
		p=new Node;
		p->next=La->next;
		La->next=p;
		printf( "请输入表A中第%d个元素值(不需要按大小顺序输入,系统会自动排序):",i+1 );
		scanf( "%d",&p->data );
	}   
	printf("表A生成成功!\n");
	insertSort(La); 
	display(La);
	Node *a=A->next;//表A首结点为a 
		
	//生成表B,同上 
	while(1)
	{
		printf( "请输入链表B的元素个数(>0):" );
		scanf( "%d",&B_length );//自定个数
		if(B_length>0)
			break;
		printf( "输入有误,请重新输入!\n" );
		wait();
		printf( "\n" );	
	}

	//向线性表插入length个新结点 
	for(i=0;i<B_length;i++)		//自定数据 
	{
		p=new Node;
		p->next=Lb->next;
		Lb->next=p;
		printf( "请输入表B中第%d个元素值(不需要按大小顺序输入,系统会自动排序):",i+1 );
		scanf( "%d",&p->data );
	}   
	printf("表B生成成功!\n");
	insertSort(Lb); 
	display(Lb);
	Node *b=B->next;//表B首结点为b 
	
	p=Lc;//使C成空表 
	while (p->next != NULL)
	{
		q=p->next;
		p->next=q->next;
		free(q);
	}
	//开始合并
	p=La; 
	q=Lb; 
	r=Lc;
	
	while(p->next && q->next)//当 p,q的next均非空 
	{
		//p->next->next 首先不为空才会进行以下循环 
		while(p->next->next != NULL && p->next->data == p->next->next->data)//如果有相同的数据,指针就后移到相同数据的最后一个 
		{
			p=p->next;
		}
		while(q->next->next != NULL && q->next->data == q->next->next->data)
		{
			q=q->next;
		}
		if(p->next->data < q->next->data)
		{
			r->next=p->next;
			r=r->next;
			p->next=p->next->next;//合并时,A,B应该删去已经在C中的结点
		}
		else 
			if(p->next->data > q->next->data)
			{
				r->next=q->next;
				r=r->next;
				q->next=q->next->next;
			}
			else //p,q的next->data相等时,任意取一个结点给r->next,并且p,q的next都要指向下一个结点 
				{
					r->next=p->next;
					r=r->next;
					p->next=p->next->next;
					q->next=q->next->next;
				}
				 
	} 
	
	//当有一表未遍历完时,将未遍历的表剩下的不重复数据全部插到C表中
	while(p->next != NULL)
	{
		r->next=p->next;
		r=r->next;
		p->next=p->next->next; 
	}
	while(q->next != NULL) 
	{
		r->next=q->next;
		r=r->next;
		q->next=q->next->next;
	}
	
	//删除后,A,B表应该置空 
	while (La->next != NULL)
	{
		q=La->next;
		La->next=q->next;
		free(q);
	}
	while (Lb->next != NULL)
	{
		q=Lb->next;
		Lb->next=q->next;
		free(q);
	}

	Node *c=Lc->next;//表C首结点指针为c;
	printf( "A,B成功合并为表C,结果如下:\n" );	 
}

// 将一个首结点指针为a的单链表A分解成两个单链表A和B,其首结点指针分别为a和b,
//使得链表A中含有原链表A中序号为奇数的元素,而链表B中含有原链表A中序号为偶数的元素,且保持原来的相对顺序。
void divideList(Node *A,Node *B)
{
	printf( "需要您先初始化A表:\n" );
	init(A);
	if(A->next == NULL)
	{
		printf( "空表不能划分,请初始化为非空表后再试!\n" );
		return;
	} 
	display(A);
	
	Node *a=A->next,*b;//a指向A表首元结点 
	Node *p=a,*q=B;//p指向A的首元结点,q指向B头结点 
	
	while(p->next != NULL)//p初始指向首元结点,最后指向倒数第二个结点 
	{						//p->next初始指向第二个结点 
		q->next=p->next;//将偶数序号结点接到B表中 
		q=q->next;//q指向新接上的结点 
		p->next=p->next->next; //把已经接到B的结点在A中删除,也就是把下一个奇数序号的结点接到p之后 
		if(p->next != NULL)		//如果A中删除偶数序号的结点后,p指向的结点变成了最后一个,p就不用后移了 
			p=p->next;			//如果p没有指向最后一个结点,p就要后移,也就是指向原来A表中下一个奇数序号的结点 
		q->next=NULL; //将B表中最后一个结点的指针域置空 
	}  
	
	b=B->next;// b指向B的首元结点 
	printf( "单链表已分解成两个单链表A和B,\nA中含有原链表A中序号为奇数的元素,\n而链表B中含有原链表A中序号为偶数的元素,\n且保持原来的相对顺序;\n" );
	printf( "表A如下:\n" );
	display(A);
	printf( "表B如下:\n" );
	display(B); 
}



/*



    6、将链接存储线性表逆置,即最后一个结点变成第1个结点,原来倒数第2个结点变成第2个结点,如此等等。
    7、生成有序的两个单链表A和B(链表的数据和个数自定),其首结点指针分别为a和b,要求将两
个单链表合并为一个有序的单链表C,其首结点指针为c,并且合并后的单链表的数据不重复。   
    8、将一个首结点指针为a的单链表A分解成两个单链表A和B,其首结点指针分别为a和b,使得链表A中含有原链表A中序号为奇数的元素,而链表B中含有原链表A中序号为偶数的元素,且保持原来的相对顺序。
    
*/

链栈的基本操作及进制转换

//链栈的基本操作及进制转换
#include <iostream>
using namespace std;
#include <iomanip>
#include <conio.h>
#include <stdio.h>
#include <process.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>

#define TRUE 1
#define FALSE 0

//定义结点类型 
typedef struct stackNode
{
	int data;
	struct stackNode *next;
}stackNode;

//定义链栈 (使用没有头结点的链栈) 
typedef struct linkStack
{
	stackNode *top;//栈顶指针 
	int size;//栈中元素个数 
}linkStack;

linkStack *s=NULL;//栈s 
linkStack *s1=NULL;//十转八中用到的栈 

int e;//存放栈中元素 

void initStack(linkStack* &s) //构造一个空栈s 
{
	s=new linkStack;
	s->top=NULL;
	s->size=0;
}

int clearStack(linkStack* &s)//清空栈s 
{
	stackNode *q;
	
	if(s == NULL)
	{
		return FALSE; 
	}
	
	while(s->top)//从栈顶到栈底一个个删除 
	{
		q=s->top;
		s->top=q->next;
		s->size--; 
		free(q);
	}//循环结束后,链栈为空 
	return TRUE;	
}

int destroyStack(linkStack* &s)
{
		if(s == NULL)
	{
		return FALSE; 
	}
	
	clearStack(s);
	free(s); 
	s=NULL;
	
	return TRUE;
}

bool emptyStack(linkStack *s)
{
	if(s->top)//链栈存在且非空 
	{
		return FALSE;//非空 
	}
	else 
	{
		return TRUE;//空 
	}
		
}

int stackLength(linkStack *s)
{
	return  s->size; 
}

int getTop(linkStack* &s,int &e)//取栈顶元素 
{
	if(s == NULL)
	{
		printf( "链栈不存在!请初始化之后再试!\n" );
		return FALSE; 
	}
	
	if(s->top == NULL)
	{
		printf( "链栈为空!无法取栈顶元素!\n" ); 
		return FALSE;
	} 
	e=s->top->data;
	return TRUE;
}

int Push(linkStack* &s,int e)//入栈 
{
	if(s == NULL)
	{
		return FALSE; 
	}
	
	stackNode *q; 

	q=new stackNode;
	q->data=e;
	q->next=s->top;
	s->top=q;
	s->size++;//入栈后,元素个数要加一 
	return TRUE;
} 

int Pop(linkStack* &s,int &e)
{
	if(s == NULL)
	{
		return FALSE; 
	}	
	
	if(s->top == NULL)
	{
		return FALSE;
	}
	stackNode *q;
	e=s->top->data;
	q=s->top;
	s->top=q->next;
	free(q);
	s->size--; 
	return TRUE;
}

//显示元素格式 
void visit(int e)
{
	printf( "%-4d\n",e );
}


//遍历显示输出
int stackTraverse(linkStack* s) 
{
		if(s == NULL)
	{
		printf( "链栈不存在!请初始化之后再试!\n" );
		return FALSE; 
	}	
	if(s->top == NULL)
	{
		printf( "链栈为空!\n" ); 
		return FALSE;
	}	
	
	printf( "链栈元素如下(从上往下,依次为从栈顶到栈底):\n" ); 
	stackNode *p;
	p=s->top;
	while(p)
	{
		visit(p->data);
		p=p->next;
	} 
	return TRUE;
}

void conversion()//十转八 
{
	cout << "\n\n\n\n";
	cout << "\t\t       非负十进制数转化为八进制数  \n";
	cout << "\t\t======================================\n\n";
	int N; 
	initStack(s1); 
	while(1)
	{
		cout << "\t\t请输入一个非负十进制数:";
		scanf( "%d",&N );
		if( N>=0 )
		break;
		printf( "\t\t输入有误,请重新输入!\n" ); 	
	}
	
	while(N)
	{
		Push(s1,N%8);
		N/=8;
	}
	
	printf( "\t\t转化成功!\n" );
	printf( "\t\t生成的八进制数为:" ); 
	
	while( !emptyStack(s1) )//当栈非空时,出栈
	{
		Pop(s1,e);
		printf( "%d",e );
	} 
	destroyStack(s1);//用完后销毁栈,s1置空 
	printf( "\n\t\t" );
} 

int main ()
{
	int choice;
	
	while (1)
	{
		system("cls");
		cout << "\n\n\n\n";
		cout << "\t\t              链栈的基本操作  \n";
		cout << "\t\t======================================";
		cout << "\n\n";
		cout << "\t\t              1:构造一个空栈s      \n";
		cout << "\t\t              2:把s置为空栈        \n";
		cout << "\t\t              3:销毁栈s    \n";
		cout << "\t\t              4:判断栈s是否为空        \n";
		cout << "\t\t              5:求栈s的元素个数        \n";
		cout << "\t\t              6:取栈顶元素        \n";
		cout << "\t\t              7:入栈        \n";
		cout << "\t\t              8:出栈        \n";
		cout << "\t\t              9:遍历栈s,查看栈s中所有元素        \n";	
		cout << "\t\t             10:十进制转化为八进制       \n";			
		cout << "\t\t              0:退出        \n";
		cout << "\n";
		cout << "\t\t请选择:" << flush;

		scanf( "%d",&choice );
		system("cls");
		
		switch(choice)
		{
			case 1:
				initStack(s);
				printf( "构建链栈成功!\n" );
				system( "pause" );
				break; 
			case 2:
				if( clearStack(s) )
					printf( "链栈已被清空!\n" );
				else
					printf( "链栈不存在!无法清空!\n" );
				system( "pause" );
				break;
			case 3:
				if( destroyStack(s) )
					printf( "链栈已被销毁!\n" );
				else
					printf( "链栈不存在!无法销毁!\n" );
				system( "pause" );
				break;
			case 4:
				if(s == NULL)
				{
					printf( "链栈不存在!请初始化之后再试!\n" );
					system( "pause" );
					break;
				}
				if ( emptyStack(s) )
					printf( "链栈为空!\n" ); 
				else 
					printf( "链栈非空!\n" );
				system( "pause" );
				break;
			case 5: 
				if(s == NULL)
				{	
					printf( "链栈不存在!请初始化之后再试!\n" );
					system( "pause" );
					break; 
				}
				printf( "栈s中元素个数为%d\n",stackLength(s) );
				system( "pause" );
				break;
			case 6:
				if( getTop(s,e) )
					printf( "栈顶元素为:%d\n",e ); 
				system( "pause" );
				break;
			case 7:
				if(s == NULL)
				{
					printf( "链栈不存在!请初始化之后再试!\n" );
					system( "pause" );				
					break; 
				}
				printf( "请输入要入栈的元素值:") ;
				scanf( "%d",&e );
				Push(s,e);
				printf( "入栈成功!\n元素%d已入栈!\n",e );
				stackTraverse(s);
				system( "pause" );
				break;
			case 8:
				if( Pop(s,e) )
				{
					printf( "出栈成功!\n栈顶元素:%d已出栈!\n",e );
					stackTraverse(s);
				}
				else 
					printf( "链栈不存在或链栈为空,无法出栈!\n" ); 
				system( "pause" );
				break;
			case 9:
				stackTraverse(s);
				system( "pause" ); 
				break;
			case 10:
				conversion(); 
				system( "pause" ); 
				break;
			case 0:
				exit(0);
			default :
				printf( "输入有误,请重试!\n" );
				system( "pause" );
				break;
		}
	}
	return 0;
} 

实验报告

实验二:链表的基本操作

一、实验二需要我们实现如下的要求:

1、实现单链表的创建;

    2、实现单链表的显示;

    3、实现单链表的查找(显示比较次数);

    4、实现单链表的插入;

    5、实现单链表的删除(显示比较次数);

    6、对已创建的链表(数据不限)进行直接插入排序;

    7、将链接存储线性表逆置,即最后一个结点变成第1个结点,原来倒数第2个结点变成第2个结点,如此等等;

    8、生成有序的两个单链表A和B(链表的数据和个数自定),其首结点指针分别为a和b,要求将两个单链表合并为一个有序的单链表C,其首结点指针为c,并且合并后的单链表的数据不重复;

    9、将一个首结点指针为a的单链表A分解成两个单链表A和B,其首结点指针分别为a和b,使得链表A中含有原链表A中序号为奇数的元素,而链表B中含有原链表A中序号为偶数的元素,且保持原来的相对顺序;

    10、请编程实现链栈的基本操作函数,并通过调用这些基本函数,实现十进制和八进制转换的功能。

       1是在已给的框架上稍作修改写的,2到9是自主编写,其中,8是根据我在实验一中合并顺序表的经验编写的;10是参考CSDN、书本上面的代码的基础上编写的。(1到9在一个cpp文件中,10在另一个cpp文件中)

二、遇到的问题及其相关解决措施,独特做法

       1、一开始在在关于链表的删除操作时,用p->data与要删除的数值比较,循环结束后,指针p指向的就是要删除的结点,没有指向要删除的结点的前一个结点,导致无法删除,后来用p->next->data与要删除的数值作比较,就解决了这个问题。

       2、一开始对链表的直接插入排序很为难,因为学过的直接插入排序是针对顺序表的,后来仔细想一想直接插入排序的思想,就把链表运用这个思想排序,但是编写的第一遍后出现了问题——结点插到前面应该在的位置后,在原来的位置仍然存在,没有删除,这就形成了一个环,导致排序不成功。用一个指针暂存要删除的结点,然后就在原位置删除此结点,在将它插到前面应该在的位置,就解决了上述问题。

       3、链表逆置:表中元素少于2时不进行逆置,从第二个元素结点向后遍历,将首元结点的指针域置空,依次将第二个元素结点到最后一个元素结点插入到头结点之后。

       4、生成并合并两个表需要自定数据和个数,在初始化函数的基础上修改修改,就能实现自定义了;实现在单个链表中遇到重复元素的就把指针后移到重复元素的最后一个的循环语句中,如果p->next->next != NULL写在了循环判断条件中&&的右边,程序就会崩溃,因为当p->next->next为空时(即p指向倒数第二个结点时),p->next->next->data在&&的左边就会先用到,这时,用到了空指针的数据域,程序崩溃,写成下图所示的样子就不会崩溃了;刚开始A,B中元素合并到C时,我又忘记把插到C中的结点在A,B中删除,导致在运行一次exe文件的过程中,使用两次合并时,第二次合并时程序崩溃(因为可能用到了空指针,ABC表连到了一起,结点关系错乱),后来在合并时就把已经合并到C中的结点在原表中删除,并且把清空A,B表的代码从生成A,B表的部分移到了函数的最后部分,就解决了这个问题。

       5、在第九个问题中我一开始用coun%2来判断遍历到的节点是不是偶数序号的,后来发现不需要如此操作,用了count反而出错,因为在A中每删除一个偶数序号的结点(插入到B中),指针p的下一个结点就是下一个奇数序号的结点,因此在删除之后,p直接后移就可以了,p后移后,p->next就指向了下一个偶数序号的结点。

       6、一开始把握不了链栈的定义,感觉无从下手,后来在书上只发现了链栈的结构示意图,没有发现链栈具体的定义代码,那个示意图中的链栈没有头结点,于是我写的就是没有头结点的链栈。

       7、后来在CSDN上,我发现了链栈的定义代码,它们有的只有结点的定义(只用一个指向首元结点的指针s来表示栈),没有对栈做个定义;有的对栈和指针都进行了定义(指针s表示栈,s指向的结构体包括栈顶指针top和栈中元素的个数),前者栈销毁和栈清空后都是s等于NULL,没有区别,由于链栈的清空和连链栈的销毁是不同的(清空只是置为空表,而销毁后,就无法使用这个栈了(表示栈的指针置空),如果要使用,就必须要在进行一次初始化),于是我选择了后一种定义。

       8、刚开始单纯地把栈的指针作为函数参数来进行构造空栈等需要改变栈原有形态的操作,发现使用完函数后栈依旧没有变化,于是就使用c++中的引用,来确保对栈进行一些改变其原有形态的操作可以成功。

       9、刚该开始写链栈的基本操作的函数时,写了太多的文字提示在函数中,后来发现十进制转八进制需要调用函数,于是就把文字提示转移到主函数中了,这样,在十转八中就可以正常使用了。

       10、十转八操作中,为确保用户输入的是非负数,使用了while(1)循环,用户如果输入了非负数就退出循环,进行下面的转化操作,如果输入了负数就一直在循环中,提示用户输入有误,需要用户重新输入。

       11、十转八和链栈的基本操作中用到的栈不同,分别为s和s1,这样进行了十转八操作,对栈s也是没有影响的。

三、界面展示&过程说明

下面是程序的过程执行过程的界面(因为代码垄长,所以请老师移步到.cpp文件中查看):

   初始界面(打开exe文件,进行1初始化操作之后,8和9之间莫名其妙空出了一行,原因未知

(未作说明时,都沿用上一步的数据)

1、初始化单链表

       2、显示

       3、单个插入到链表末尾

4、查找成功与查找失败

       5、删除成功与删除失败

    
       6、直接插入排序

       7、就地逆置

(8,9操作都是各自新生成的数据,不需要初始化)

       8、有序合并AB

  9、将A链表中的奇数序号元素和偶数序号元素分到两个表中

           
在查找、删除、排序、逆置操作 中需要表非空,如果表为空,会有相应提示

      

       10、链栈的基本操作及十进制转八进制(单独的cpp)

       (1)选择1链栈的构建

       (2)多次选择7入栈

       (2)然后选择一次8出栈

       (3)然后选择6查看栈顶元素

       (4)然后选择5查看元素个数

       (5)然后选择4判断栈是否为空

       (6)然后选择9查看栈中所有元素

       (7)然后选择2清空栈

       (8)然后选择4判断栈是否为空

       (9)然后再次选择5查看元素个数

       (10)然后选择3销毁栈

       (11)销毁后,如果不进行初始化,栈s就不存在了,如果再执行2到9的操作,就会出现如下提示中的一种

(12)十进制转化为八进制

四、体会与心得

       在对链式存储结构进行操作时,一定要细心,要防止出现使用空指针的数据域或指针域的情况,一旦有了这种情况,程序就会崩溃;在写某个操作时,一定要注意是不是需要在原表中进行删除操作,指针在某一步骤中还需不需要后移,处理如有不慎,就很有可能导致结点关系混乱,甚至程序崩溃。  

       细节很重要,对于一些基础概念(如链栈的结构定义)不清楚的,一定要先查清楚,再进行相关代码的编写,对于一些很小的细节也要特别注意,因为细节往往能决定程序是否能正常执行功能。

       某些操作的代码自己会写,也应该在写完后去看看别人的代码,看看自己有没有什么不足之处。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值