数据结构01-单链表(C++)

单链表的定义以及概念与结构

    单链表是一种常见的数据结构,用于存储一系列元素。它由一系列称为节点(node)的对象组成,每个节点包含两个部分:数据部分和指针部分。数据部分存储该节点表示的元素的值。指针部分则指向下一个节点,在单链表中,每个节点只有一个指针指向下一个节点,最后一个节点的指针指向空(NULL)。通过这种方式,节点之间形成了一个链式的关系,因此称为链表。在单链表中,只能通过从头节点(第一个节点)开始顺序遍历整个链表才能访问到各个节点。

   结构

typedef struct node  //typedef是函数的重命名 
{
	int data;         //存储结点,包括 data域和 next域 
	struct node *next;
}LNode ,*LinkList; 

单链表的特点

     链式存储结构;动态性;顺序访问;插入和删除效率高;内存消耗。

单链表的初始化:

     当我们开始使用单链表时,首先要对单链表进行初始化,创建一个带头结点的单链表。带头节点的单链表指的是在链表的头部添加一个空节点,该节点不存储实际的数据,仅用作头节点的前一个节点,也称为哨兵节点或者虚拟节点

代码实现:

void InitList(LinkList &L)//建立带头结点的单链表 
{
	L=new LNode;
	L->next=NULL;
 } 

单链表的建立

       头插法:    

        单链表的头插法(Head Insertion)是指在链表的头部插入新的节点的一种操作方法。具体步骤如下:

  1. 创建一个新节点,将要插入的数据存储在该节点中。
  2. 将新节点的指针指向原来链表的头节点(即新节点的 next 指针指向原来的头节点)。
  3. 将链表的头节点指向新节点(即头指针指向新节点)。

 代码实现:

 //头插法建表   输入结果与输出结果相反 
void InputList_H(LinkList &L,int n)
 {
 	LNode *p;                            //用于专门开辟新的节点,用来插入节点                     
	cout<<"请输入这"<<n<<"个数:";                           
 	for(int i=1;i<=n;i++)
 	{
 		p=new LNode;                     //申请链表LNode的空间                         
 		cin>>p->data;
 		p->next = L->next;              //将新结点插入头节点后面
 		L->next = p;
	 }
}

        尾插法:

         单链表的尾插法(Tail Insertion)是指在链表的尾部插入新的节点的一种操作方法。具体步骤如下:

  1. 创建一个新节点,将要插入的数据存储在该节点中。
  2. 若链表为空,则将链表的头节点指向新节点(即头指针指向新节点),并将新节点作为尾节点。
  3. 若链表不为空,则找到当前链表的尾节点(即遍历到 next 指针为 null 的节点)。
  4. 将尾节点的 next 指针指向新节点,将新节点作为新的尾节点。

   

代码实现:

//尾插法建表  输入顺序与输出顺序一致 
void InputList_R(LinkList &L,int n)
{
	LNode *p;                         //用于专门开辟新的结点
	LNode *r;                         //用于指向表尾结点 
	r=L; 
	while(r->next!=NULL)              //先遍历链表在插入 
	{
		r=r->next;
	}
	cout<<"请输入这"<<n<<"个数:";
	for(int i=1;i<=n;i++)
	{
		p=new LNode;                  //申请链表LNode的空间
		cin>>p->data; 
		//将新结点插入表尾 
		r->next=p;
		r=p;        //将新插入的结点的指针p变为指针r,把插入的结点变为新的尾结点 
	}
	r->next=NULL;
}

单链表的遍历:

    代码思路:我们要遍历一个单链表。首先是要创建一个用于遍历节点的指针p,它最开始是指向表头下一个节点,然后输出该节点data域的数值,随后在 p=p->next,让p又指向当前节点的下一个节点,直到没有

   代码实现:

//链表遍历
void ShowList(LinkList &L) 
{
	LNode *p;   //用于专门开辟新的结点
	p=L->next;  //先将遍历指针指向头部,用头部往下进行遍历 
	cout<<"它的值如下:"<<endl;
	while(p!=NULL)
	{
		cout<<p->data<<"\t";
		p=p->next; 
	 } 
}

单链表的长度:

代码思路:其实与单链表的遍历的思路是一致的,不过要多一个整数

代码实现:

//计算表长    通过遍历的方法进行计算表的长度 
void GetLength(LinkList &L)
{
	LNode *p;   //用于专门开辟新的结点
	p=L->next;  //先将遍历指针指向头部,用头部往下进行遍历
	int i=0;      //头结点不做计算   
	while(p!=NULL)
	{
		p=p->next;
		i=i+1; 
	 } 
	 cout<<"该表长是:"<<i<<endl;
 } 

单链表的按位查找:

查找链表中第n个位置的data值

代码思路:首先是要创建一个用于遍历节点的指针p,p指针用于指向第n个位置节点,所以我们要对链表进行依次遍历,直到p指向第n个位置,然后输出该节点下的数值

代码实现:

 //按位置进行查找
void ShowOne(LinkList &L,int n)
{
	LNode *p;            //用于专门开辟新的结点
	p=L->next;           //要向下进行遍历,所以该指针要先指向第一个(头结点的下一个) 
	for(int i=1;i<n;i++)
	{
		p=p->next;
	}
	cout<<"这个位置的值是:"<<p->data<<endl;
 } 
 

单链表的按位删除:

代码思路:首先是要创建一个用于遍历节点的指针p,另外创建一个指向被删除节点的指针q,当我们删除q指向的节点后,我们会发现链表已经断开,此时我们便要通过q节点以及p节点来恢复链表,删除前要想将p的下一个节点变成q的下一个节点也就是p->next=q->next。

代码实现:

 //按位置删除
 void DelList_W(LinkList &L,int n)
 {
 	LNode *p,*q;     //开辟两个新的指针,一个用于先遍历查找到要删除的结点, 
 	p=L;
 	for(int i=1;i<n;i++)
 	{
 		p=p->next;
	 }
	 q=p->next;                 //此时的q正指向要删除的的结点 
	 p->next=q->next;           //q指的是被删除的结点的前驱,删除q后,q后续的结点要向前 
	 delete(q);                 //删除q 
	 while(p->next!=NULL)       //将q后续的结点推向前 
	 {
	 	p=p->next;
	 }
  } 
  

单链表的按位插入:

代码思路:首先也是创建两个指针,一个遍历p,另外一个用于创建一个新的节点q,当我们遍历到要插入的位置时,则我们需要先把q的next指向p的next,然后将p的next重新指向q。

代码实现:

void DelList_I(LinkList &L,int n)
 {
 	LNode *p,*q;     //开辟两个新的指针,一个用于遍历,查一个用于指向开辟的新节点 
 	p=L;
 	for(int i=1;i<n;i++)
 	{
 		p=p->next;
	 }
	 q=new LNode;
	 cout<<"请输入你要插入数据:";
	 int m;
	 cin>>m;
	 q->data=m; 
	 q->next=p->next;              //将q节点指向p的next节点
	 p->next=q;                    //将p的next指向q,实现按位插入
  } 

还有部分操作较为简单不一一展示。

源码部分:

第一步一定要进行初始化哦,还有得是,源码使用的是头插法建表,如果想用尾插法建表,只需要修改main方法中的 case 2部分哦(修改方式应该一眼就会,我就不过多解释了)。

#include<iostream>
using namespace std;

typedef struct node  //typedef是函数的重命名 
{
	int data;         //存储结点,包括 data域和 next域 
	struct node *next;
}LNode ,*LinkList; 
//建立带头结点的单链表 
void InitList(LinkList &L)
{
	L=new LNode;
	L->next=NULL;
 } 
 
 //头插法建表   输入结果与输出结果相反 
void InputList_H(LinkList &L,int n)
 {
 	LNode *p;                            //用于专门开辟新的结点                     
	cout<<"请输入这"<<n<<"个数:";                           
 	for(int i=1;i<=n;i++)
 	{
 		p=new LNode;                     //申请链表LNode的空间                         
 		cin>>p->data;
 		//将新结点插入头节点后面
 		p->next = L->next;
 		L->next = p;
	 }
}


//尾插法建表  输入顺序与输出顺序一致 
void InputList_R(LinkList &L,int n)
{
	LNode *p;                         //用于专门开辟新的结点
	LNode *r;                         //用于指向表尾结点 
	r=L; 
	while(r->next!=NULL)              //先遍历链表在插入 
	{
		r=r->next;
	}
	cout<<"请输入这"<<n<<"个数:";
	for(int i=1;i<=n;i++)
	{
		p=new LNode;                  //申请链表LNode的空间
		cin>>p->data; 
		//将新结点插入表尾 
		r->next=p;
		r=p;        //将新插入的结点的指针p变为指针r,把插入的结点变为新的尾结点 
	}
	r->next=NULL;
}


//链表显示
void ShowList(LinkList &L) 
{
	LNode *p;   //用于专门开辟新的结点
	p=L->next;  //先将遍历指针指向头部,用头部往下进行遍历 
	cout<<"它的值如下:"<<endl;
	while(p!=NULL)
	{
		cout<<p->data<<"\t";
		p=p->next; 
	 } 
}	

//计算表长    通过遍历的方法进行计算表的长度 
void GetLength(LinkList &L)
{
	LNode *p;   //用于专门开辟新的结点
	p=L->next;  //先将遍历指针指向头部,用头部往下进行遍历
	int i=0;      //头结点不做计算   
	while(p!=NULL)
	{
		p=p->next;
		i=i+1; 
	 } 
	 cout<<"该表长是:"<<i<<endl;
 } 
 
 //在表尾进行出入 
 void InsertR(LinkList &L)
 {
 	LNode *p;                         //用于专门开辟新的结点
 	p =new LNode;                     //申请链表LNode的空间 
 	p->next=NULL;
	 cout<<"请输入插入的值:";
	 cin>>p->data;
	 LNode *r=L;                   //通过r的遍历找到表尾结点 
	 while(r->next!=NULL)
	 {
	 	r=r->next;
	  } 
	  r->next=p;
 }
 
	 //按位置进行查找
	void ShowOne(LinkList &L,int n)
	{
		LNode *p;            //用于专门开辟新的结点
		p=L->next;           //要向下进行遍历,所以该指针要先指向第一个(头结点的下一个) 
		for(int i=1;i<n;i++)
		{
			p=p->next;
		}
		cout<<"这个位置的值是:"<<p->data<<endl;
	 } 
	 
 
 //按位置删除
 void DelList_W(LinkList &L,int n)
 {
 	LNode *p,*q;     //开辟两个新的指针,一个用于先遍历查找到要删除的结点, 
 	p=L;
 	for(int i=1;i<n;i++)
 	{
 		p=p->next;
	 }
	 q=p->next;                 //此时的q正指向要删除的的结点 
	 p->next=q->next;           //q指的是被删除的结点的前驱,删除q后,q后续的结点要向前 
	 delete(q);                 //删除q 
	 while(p->next!=NULL)       //将q后续的结点推向前 
	 {
	 	p=p->next;
	 }
  } 
  //安元素删除
  void DelList_Y(LinkList &L,int n)
  {
  	LNode *p,*q;
  	p=L->next;
  	while(p->data!=n)
  	{
  		p=p->next;
	  }
	   p=q->next;                 //此时的p正指向要删除的的结点 
	 q->next=p->next;           //q指的是被删除的结点的前驱,删除p后,p后续的结点要向前 
	 delete(p);                 //删除p 
	 while(q->next!=NULL)       //将p后续的结点推向前 
	 {
	 	q=q->next;
	 }
   } 
    //按位置插入 
 void DelList_I(LinkList &L,int n)
 {
 	LNode *p,*q;     //开辟两个新的指针,一个用于遍历,查一个用于指向开辟的新节点 
 	p=L;
 	for(int i=1;i<n;i++)
 	{
 		p=p->next;
	 }
	 q=new LNode;
	 cout<<"请输入你要插入数据:";
	 int m;
	 cin>>m;
	 q->data=m; 
	 q->next=p->next;
	 p->next=q; 
  } 
int main()
{
	LNode *head;
	/*LinkList head;*/                 //LinList head    等价于   LNode *head 
	int n;
	do
	{
		cout<<"\t1 初始化"<<endl;
		cout<<"\t2 输入"<<endl;
		cout<<"\t3 显示"<<endl;
		cout<<"\t4 长度"<<endl;
		cout<<"\t5 尾插入"<<endl;
		cout<<"\t6 位置查找"<<endl;
		cout<<"\t7 按位置删除"<<endl;
		cout<<"\t8 按位置插入"<<endl;
		cout<<"\t9 删除某个元素"<<endl;
		cout<<"\t0 退出操作"<<endl;
		cout<<"  选择: ";
		cin>>n;
		
		switch(n)
			{
			case 1:
				InitList(head);     //创建单链表 
			    cout<<endl;
			    break;
			case 2:
				int m;
			    cout<<"\n\t请输入数据量:";
			    cin>>m;
			    InputList_H(head,m);       //头插法 
			    //InputList_R(head,d);   //尾插法 
			    cout<<endl;
			    break;
			case 3:
				ShowList(head);  //链表的显示 
		 	    cout<<endl<<endl;
			 	break;
			case 4:
				GetLength(head);    //计算链表长度 
				break;
			case 5:
				InsertR(head);   //在表尾借进行插入 
				break;
			case 6:
				int d;
		 	    cout<<"请输入查找的位置:";
		 	    cin>>d;
		 	    ShowOne(head,d);     //位置查找 
		 	    break;
		 	case 7:
		 		int p;
		 	    cout<<"请输入删除的位置:";
		 	    cin>>p;
		 	    DelList_W(head,p);   //位置删除 
		 	    break;
		 	case 8:
		 		int q;
		 		cout<<"请输入插入的位置:";
		 		cin>>q;
		 		DelList_I(head,q);
		 		break;
		 	case 9:
		 		int a;
		 	    cout<<"请输入删除的元素:";
		 	    cin>>a;
		 	    DelList_Y(head,a);  //元素删除 
		 	    break;
		 	default:
		 		break;
			}

	}while(n!=0);
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值