数据结构之单链表

发布于:2022-07-26 ⋅ 阅读:(649) ⋅ 点赞:(0)

  从这个暑假开始,学完了基本算法的我,已经开启了数据结构之旅,第一个数据结构是“线性表”,分为链式存储结构和顺序存储结构,一个是顺序表(相当于数组),另一个是链表,分为单链表、双向链表、循环链表(用指针来进行和顺序表一样的操作),在这两个之中,顺序表较为简单,链表就是比较绕,很多人可能会忘记些什么。

  而我,在这个星期里面,一步一步听课,写代码,学会了关于单链表的许多操作,今天,我就来写一个关于单链表的总结!

  首先,我们需要学会怎么样来定义一个单链表!

  • 我们要知道一个链表的长度是多少。可以用length来表示。
  • 我们还要知道这个链表的数据域是什么。用data来表示。
  • 最后还需要知道这个链表的指针域指向的哪里。用nxet来表示 ,next代表着链表的指针域,所以是一个指针,需要加上*号。

  这样,我们就可以定义一个结构(struct)了,用Node来表示这个结构

定义一个链表代码:

struct Node
{
    int data;
    int length;
	struct Node* next;    	
};

  现在,只需要在气体地方写上Node *L,就代表创建了一个链表! 

  定义了一个链表后,需要先来初始化后才可以使用,在这里,可以使用new函数,意思是分配一个空间,那我们要将空间分配给谁呢?自然是给这个结构了,这个链表有了空间之后,我们需要将这个链表设置为空,那么只需要让这个链表的指针域指向NULL(表示为空)就行了!

链表初始化代码:

bool initList_L(Node *&L)//链表初始化 
{
	L=new Node;
	L->next=NULL;
	return true;
}

  指针一般都以->代表指向着什么东西!

  定义链表后,又初始化了链表,接下来就可以使用链表了,我们的第一个操作就是判断这个链表是不是空链表(指针域指向空),怎么判断呢,只需要判断一下这个链表的指针域是不是指向空的,是就返回true,不是就代表这个链表的指针域指向了别的东西,返回false。

判断当前链表是否为空代码:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
struct Node
{
    int data;
    int length;
	struct Node* next;    	
};
bool initList_L(Node *&L)//链表初始化 
{
	L=new Node;
	L->next=NULL;
	return true;
}
bool ListEmpty(Node *L)//判断链表是否为空 
{
	if(L->next)
	  return false;
	else
	  return true;
}
int main()
{
    Node *p;
    initList_L(p);
    if(ListEmpty(p)==1)
      cout<<"当前链表为空"<<endl;
    else
      cout<<"当前链表不为空"<<endl;
    return 0;
}

  之后,我们来学习下一个操作,来销毁这个链表,怎么销毁呢,可以用delete,这个是来销毁当前这个指针指向的地方,我们只需要一个while循环,只要不是指向空的,那么就进行销毁,先定义一个链表p,将p=L;然后p=p->next代表指针p成为了p指向的数据域,然后进行销毁。

销毁当前链表代码:

bool DestroyList_L(Node *&L)//销毁链表 
{
	Node *p;
	while(L!=NULL)
	{
		p=L;
		L=L->next;
		delete p;
	}
	return true;
} 

  由于我们还没有学习建立链表,这个函数的功能在之后实现! 

  然后,我们继续来看下一个操作,清空这个链表,就是将L重新设定为空表,这可和初始化不同,因为里面是有数据的,要想清空链表,需要多定义两个链表Node *p,*q;将p=L,然后用while循环知道直到链表为空,然后q=p->next代表指针q成为了p指向的数据域,注意这个是q,然后delete p,直接就销毁了,然后将p=q,相当于重新又定义了一个链表,不过里面的数据被销毁了!

请空当前链表代码:

bool ClearList(Node *&L)//清空链表 
{
	Node *p,*q;
	p=L->next;
	while(p)
	{
		q=p->next;
		delete p;
		p=q;
	}
	L->next=NULL;
	return true;
}

  要想测验这个函数的真实作用,还是要学会建立链表,然后将一个不为空的链表“L”放进这个函数里面,执行过后,我们想知道这个链表是否真的被清空了,就可以用到上面我将的判断当前链表是否是空链表的函数!

  之后,我们就可以学习比较有难度的操作函数了,第一个就是求这个链表的表长,和之前一样,定义Node *p;让p=L;利用while循环知道指向NULL为止,里面要让指针p成为了p指向的数据域,然后在将length++,最后输出length就可以了!

求当前链表的表长代码:

bool qbc_L(Node *L)//求链表的表长 
{
	Node *p;
	p=L->next;
	while(p!=NULL)
	{
		p=p->next;
		L->length++;
	}
	L->length++;
	return true;
}

  之后来到五大难关之一,单链表的查找算法(函数),按我们输入的值来查找这个数,意思是在这个链表里面查找一个你输入的数,返回这个数的下标,首先定义一个int类型变量j,代表着在这个链表里面的下标,然后定义Node *p;p=L;, while循环直到为空,里面指针p成为了p指向的数据域,只要这个指针的数据域等于输入的这个数,就可以直接返回j,如果while循环执行完毕都没有找到,那么就返回0;

按输入的值查找代码:

int LocateElem_L(Node *L,int e)//按输入的值查找 
{
	Node *p;
//	initList_L(p);
	int j;
	p=L;
	j=1;
	while(p!=NULL)
	{
		if(p->data==e)
		  return j;
		p=p->next;
		j++;
	}
	return 0;
} 

    然后,来到五大难关之二,单链表的插入算法,函数执行效果是在这个链表下标为i的地方插入一个e,我们需要定义一个while循环,条件是指针不为空并且j<i-1, 里面指针p成为了p指向的数据域,j++,这样是来查找链表下标为i的地方,然后while结束,经过判断,我们可以先连新绳,再解旧绳,所以要重新定义一个指针Node *t,给t分配空间以后,t的数据域data用来指向e,然后t的指针域在指向p的指针域,然后将p的指针域指向t的地址,也就是e。

在当前链表插入一个下标在i的地方的数e代码:

bool insert(Node *&L,int i,int e)
{
	Node *p,*t;
	p=L;
	int j=0;
	while(p&&j<i-1)
	{
		p=p->next;
		++j;
	}
	if(!p||j>i-1)
	  return false;
	t=new Node;
	t->data=e;
	t->next=p->next;
	p->next=t;
	return true;
}

  有了插入,那当然还有删除了,就是在一个链表里面找到你输入的下标,然后将这个数给删除,和插入一样,首先找到链表下标为i的地方,经过判断,一样的思路,先连新绳,再解旧绳,由于是要删除,就需要多加一个delete q。

在当前链表删除一个下标在i的地方的数:

bool move(Node *&L,int i,int&e)
{
	Node *p,*q;
	p=L;
	int j=0;
	while(p->next&&j<i-1)
	{
		p=p->next;
		j++;
	}
	if(!(p->next)||j>i-1)
	  return false;
	q=p->next;
	p->next=q->next;
	e=q->data;
	delete q;
	return true;
}

   现在,我们已经将所以关于单链表的基本操作函数给讲解了一遍,最后就到了建立链表的时候了,建立链表有两种方法,一种是头插法,另一种是尾插法,我个人比较喜欢用尾插法,因为是正着的,而头插法确实正着输入倒着输出的。

  我们先来看头插法,首先要建立一个带有头结点的链表,生成一个新的节点,给这个节点分配空间,然后输入你要的元素值,在将元素值按照插入排序的办法插入到表头。

头插法创建链表代码:

Node *tcf(Node *L,int n)
{
	Node *p;
	L=new Node;
	L->next=NULL;
	for(int i=n;i>0;i--)
	{
		p=new Node;
		cin>>p->data;
		p->next=L->next;
		L->next=p;
	}
	return p;
}

  尾插法和这个差不多,先用尾指针r指向头结点,然后生成一个新的节点,之后输入元素值,将其插入到表尾。

尾插法创建链表代码:

Node *wcf(Node *L,int n)
{
	Node *p,*r;
	L=new Node;
	L->next=NULL;
	r=L;
	for(int i=0;i<n;i++)
	{
		p=new Node;
		cin>>p->data;
		p->next=NULL;
		r->next=p;
		r=p;	
	}
	return L;
}

  接下来,来到了我们的最后一个环节,就是输出一个链表,和之前一样,while循环到这个指针为空, 指针p成为了p指向的数据域,然后输出p的数据域内容data。

输出链表代码:

void printList(Node *p)
{
	p=p->next;
	while(p!=NULL)
	{
		printf("%d,",p->data);
		p=p->next; 
	}
	cout<<endl;
}

  最后,我把这些函数所有的都整理了一遍,可以依照以下的程序来进行实现所有的链表操作!

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
struct Node
{
    int data;
    int length;
	struct Node* next;    	
};
bool initList_L(Node *&L)//链表初始化 
{
	L=new Node;
	L->next=NULL;
	return true;
}
bool ListEmpty(Node *L)//判断链表是否为空 
{
	if(L->next)
	  return false;
	else
	  return true;
}
bool DestroyList_L(Node *&L)//销毁链表 
{
	Node *p;
	while(L!=NULL)
	{
		p=L;
		L=L->next;
		delete p;
	}
	return true;
} 
bool ClearList(Node *&L)//清空链表 
{
	Node *p,*q;
	p=L->next;
	while(p)
	{
		q=p->next;
		delete p;
		p=q;
	}
	L->next=NULL;
	return true;
}
bool qbc_L(Node *L)//求链表的表长 
{
	Node *p;
	p=L->next;
	while(p!=NULL)
	{
		p=p->next;
		L->length++;
	}
	L->length++;
	return true;
}
int LocateElem_L(Node *L,int e)//按输入的值查找 
{
	Node *p;
//	initList_L(p);
	int j;
	p=L;
	j=1;
	while(p!=NULL)
	{
		if(p->data==e)
		  return j;
		p=p->next;
		j++;
	}
	return 0;
} 
bool insert(Node *&L,int i,int e)
{
	Node *p,*t;
	p=L;
	int j=0;
	while(p&&j<i-1)
	{
		p=p->next;
		++j;
	}
	if(!p||j>i-1)
	  return false;
	t=new Node;
	t->data=e;
	t->next=p->next;
	p->next=t;
	return true;
}
bool move(Node *&L,int i,int&e)
{
	Node *p,*q;
	p=L;
	int j=0;
	while(p->next&&j<i-1)
	{
		p=p->next;
		j++;
	}
	if(!(p->next)||j>i-1)
	  return false;
	q=p->next;
	p->next=q->next;
	e=q->data;
	delete q;
	return true;
}
void printList(Node *p)
{
	p=p->next;
	while(p!=NULL)
	{
		printf("%d,",p->data);
		p=p->next; 
	}
	cout<<endl;
}
Node *tcf(Node *L,int n)
{
	Node *p;
	L=new Node;
	L->next=NULL;
	for(int i=n;i>0;i--)
	{
		p=new Node;
		cin>>p->data;
		p->next=L->next;
		L->next=p;
	}
	return p;
}
Node *wcf(Node *L,int n)
{
	Node *p,*r;
	L=new Node;
	L->next=NULL;
	r=L;
	for(int i=0;i<n;i++)
	{
		p=new Node;
		cin>>p->data;
		p->next=NULL;
		r->next=p;
		r=p;	
	}
	return L;
}
int main()
{
	Node *p;
	initList_L(p);
//No.1头插法 
//	p=tcf(p,10);
//	printList(p);
//No.2尾插法
//    p=wcf(p,10);
//	printList(p); 
/
/
/
/
//No.1判断链表是否为空 
//    cout<<endl;  
//    cout<<"当前链表:";
//    cout<<ListEmpty(p)<<endl;
//No.2销毁这个链表
//    cout<<endl;
//    cout<<"当前链表:";
//    DestroyList_L(p);
//	printList(p);
//No.3清空这个链表
//    cout<<endl;
//    cout<<"当前链表:";
//	ClearList(p);
//	printList(p);
//No.4求链表的表长
//    cout<<endl;
//	qbc_L(p);
//	cout<<"链表长度:"<<p->length;
//No.5按输入的值查找 
//    cout<<endl;
//	int n;
//	cin>>n;
//	cout<<LocateElem_L(p,n)<<endl;
//No.7删除一个数
//    cout<<endl;
//	int i,v;
//	cin>>i;
//	move(p,i,v);
//	printList(p);
//	cout<<v<<endl;
//No.6插入一个数	
//    int n,e;
//	cout<<endl;
//	cin>>n>>e;
//	insert(p,n,e);
//	printList(p);
}

 有需要的可以把屏蔽给取消,但是只能取消No.i和No.i+1之间的,不然链表可能会有错误! 

  

 


网站公告

今日签到

点亮在社区的每一天
去签到