数据结构与算法:链表知识笔记

单链表的类型声明

struct Node;
typedef struct Node* PtrToNode;
typedef PtrToNode List //header ptr
typedef PtrToNode Position //当前位置

List MakeEmpty(List L);   //制作空链表
int IsEmpty(List L);		//是否是空表
int IsLast(List L);		//是否是最后一个
Position Find(ElementType x, List L);	//找到x,返回地址
void Delete(ElementType x,List L);		//删除x
Position FindPrevious( ElementType x, List L);	//找到x前一个元素,返回地址
void Insert(ElementType x,List L, Position P);	//插入
void DeleteList (List L); 	//删除整个表
Position Header(List L); 	//返回头指针
Position First(List L);		//返回第一个元素地址
Position Advance(Position P);		//返回最后一个元素地址

struct Node
{
	ElementType element;
	Position next;
}

List MakeEmpty(List L)制作空链表

List MakeEmpty(List L)
{
	L = new struct Node;
	L->num=0;
	L->next=nullptr;
	return L;
}

动态为L(指向结构的指针)分配足够的内存空间,然后把next指针设定为指向nullptr(C++11)。最后return L。 使用时要以赋值形式使用。

void InputElement(List L) [自己额外添加的] 输入元素

void InputElem(List L)
{
	cout<<"Enter number into list,(Ctr+Z to quit)\n";
	int temp;
	Position P,previous;
	while(cin>>temp)
	{
		P = new struct Node;
		P->num = temp;
		P->next = NULL;
		if(L->next==nullptr)
		{
			L->next = P; 
			previous = P;
			}	
		previous->next = P;
		previous = P;
	}
}

手绘图解
在这里插入图片描述
int IsEmpty(List L) && int IsLast(Position P, List L)


int isEmpty(List L)
{
	return L->next==nullptr;  
}

int isLast(Position P,List L)
{
	return P->next == nullptr; 	
}

即使不用到表头L,也要有一个它的参数

Position find(int x,List L)找到链表中相关元素地址

Position find(int x,List L)
{
	Position temp;
	temp=L->next;
	while(temp!=nullptr&&temp->num!=x)
	{
		temp = temp->next;
	}
	return temp;
 } 

从first one开始,遍历链表,时间复杂度O(N)

Position FindPrevious(int x,List L) //找到元素的前一个位置

Position FindPrevious(int x,List L)
 {
 	Position P;
 	P=L;
 	while(P->next!=nullptr&&P->next->num!=x)
 		P = P->next;
 	return P;
 }

同上,时间复杂度O(N)

(重点) insert && delete

 void Delete(int x,List L)   //注意不能是小写开头哦,因为是key word
 {
 	Position P,temp;
 	P = FindPrevious(x,L);
 	if(!isLast(P,L))     //如果P是最后,则没有相关元素
 	{
 		temp = P->next;
 		P->next = temp->next;
 		delete temp;
	 }
 }
 
 void insert(int x,List L,Position P)
 {
 	Position temp;
 	temp = new struct Node ;
 	if(temp == NULL)       //一般情况不会发生...
 		cerr<<"Out of space\n";
 	temp->num=x;
 	temp->next = P->next;
 	P->next = temp;
 }

手绘图解
在这里插入图片描述

不过insert的第三个参数为Position P,链表不是数组,没有索引,所以比较麻烦。所以如果可以的话可以换成其他参数。

insert的时间复杂度为O(1),delete的时间复杂度为O(N),因为调用了findprevious().

void PrintList(List L) //索引到最后一位

void PrintList(List L)
 {
 	if(L->next==nullptr)
 	{
 		cout<<"List empty\n";
 		return;
	 }
	Position P;
	P = L->next;
	while(P->next!=nullptr)
	{
		cout<<P->num<<" ";
		P = P->next;
	}
	cout<<P->num<<endl;
 }

void DeleteList(List L) //清除链表

 void DeleteList(List L)
 {
 	Position P, temp;
 	if(L->next==nullptr) return;  //如果是空链表,return。 很重要!
 	P=L->next;
 	L->next = nullptr;
 	while(P->next!=nullptr)
 	{
 		temp = P->next;
 		delete P;
 		P=temp;
	 }
	 delete P;
  } 

这里一定要添加if(L->next == nullptr) return;如果不添加而且其他代码不改的话,那么P就是nullptr,然后你还delete P(销毁了某处内存空间块)

在头部insert或者尾部insert

void InsertBack(int x,List L)
{
	Position p,temp;
	p = L;
	while(p->next!=nullptr)
		p = p->next;
	temp = new struct Node;
	temp->num  = x;
	temp->next = p->next;
	p->next = temp;
	}	
//尾部insert就是索引到尾部,然后insert
void InsertFront(int x,List L)
{
     Position p;
     p = new struct Node;
     p->num = x;
     p->next = L->next;
     L->next = p;
}
//头部更简单些

尾指针rear

可以创建一个rear尾指针指向最后一个位置,这样就不用从到索引到尾了。

Position Rear = new struct Node;
Position temp = L;
while(temp->next != nullptr)
	temp = temp->next;
Rear-> next = temp;

链表的游标实现

链表的游标实现就是用数组来代替链表,数组有一个cursor(游标),cursor为int类型,然后数值就是下一个元素的position。因为游标实现是用数组来实现,所以有一些功能需要我们自己定义完成(比如new、delete、用int来代替指针)。其余的各个函数功能可能也有一些小修改,下面写几个function,体会一下就好

#define MAXSIZE 10
struct Node
{
	int data;
	int next;
} cursor[MAXSIZE];

typedef int List;
typedef int Position;

//初始化

void initialize(List& L)
{
	for(int i=0;i<MAXSIZE-1;i)
		cursor[i].next=i+1;
	cursor[MAXSIZE-1].next = 0;
	头结点的next为1,第一个结点next为2……
	最后一个结点的next为0(既头结点,既null)
}

//new与delete 替代

Position allocate(void)
{
	Position P;
	p = cursor[0].next;
	cursor[0].next = cursor[p].next;
	return P;
}
头结点的next总是保存下一个可用结构的位置值
void Free(Position P)
{
	cursor[p].next = cursor[0].next;
	cursor[0].next = p;
}

// 插入

void Insert(int x,List L,Position P)
 {
 	Position temp,i;
 	temp = allocate();
 	if(temp == 0)
 	{
 		cout<<"out of space!";
 		return ;
	 }
	cursor[temp].data =x;
	cursor[temp].next = cursor[p].next;
	cursor[p].next = temp;
 }

双向循环链表

双向循环链表跟单链表比起来,多的部分可能就是一个指向前续数的指针,以及特性:最末一个结点的next指向头结点,头结点的prior指向尾结点。

1.结点

struct Node
{
	int num;
	struct Node* next;
	struct Node* prior;
};

2.空链表create

List MakeEmpty()
{
	List L;
	L = new struct Node;
	L->next = nullptr;
	L->prior = nullptr;
	return L;
}

3.填充链表

void FullList(List L)
{
	Position p,q;
	int n;
	cout<<"Enter numbers (CTRL+Z to quit)\n"; 
	while(cin>>n){
		p = new struct Node;
		if(L->next == nullptr){
			L->next = p;
			p->next = L;
			L->prior = p;
			p->prior = L;
			q = p;
			q->num = n;
			continue;
		}
		p->next =L;
		p->prior=q;
		q->next=p;
		L->prior=p;
		p->num = n;
		q=p;
	;}
}

填充链表比较麻烦,得分成两部分,已有结点和没有结点。不过还是挺好理解的。

  1. 插入结点
void Insert(int x,int y,List L)  //数字x插入到y之前
{
	Position temp =L;
	Position p = new struct Node;
	p->num = x;
	while(temp->next->num!=y)
	{
		temp = temp->next;
	}
	p->next = temp->next;
	p->prior = temp;
	temp->next->prior = p;
	temp->next = p;
 } 
  1. 打印和反向打印
 void PrintList(List L)
 {
 	Position p = L;
 	while(p->next!=L){
 		cout<<p->next->num<<" ";
 		p = p->next;
 	}
 }
 
 void reversePL(List L)
 {
 	Position p = L;
 	while(p->prior!= L)
 	{
 		cout<<p->prior->num<<" ";
 		p = p->prior;
	 }
 }

注意,此时判断是否终止的条件已经从判断是否指向nullptr变成是否回到头结点!!

在这里插入图片描述

链表

应用
1. 基数排序
原理:
手绘图解
在这里插入图片描述
首先将数组中的数字按个位数,然后分别装进桶链表中。一轮完毕后,更新array同时clear桶链表,开始new round。
根据个位数、十位数、百位数……的顺序装入桶中,相当于排序,如果下一轮的下一位数两个数相同,那么进入链表的顺序就是上一轮的排序,然后就比出大小。
ps. insert使用insertback函数; 将链表集合到头文件中
ps2.0 : 排序时严格按照此顺序:创建链表、根据digit插入链表、更新array、delete链表、下一位数循环开始。然后善用调试(F5)

// 基类排序
#include"list.h"
int Getdigit(int x,int y); //得到x的第y位数字
void Radixsort(int arr[]);
void printarr(int arr[],int n);
#define N 10
#define B 10
#define S 3

int main()
{
    
    
	int array[N]{
    
    14,51,213,4,123,56,76,99,13,41};
	cout<<"before sorting:\n";
	printarr(array,N);
	Radixsort(array);
	cout<<"After sorting:\n";
	printarr(array,N);
	 } 
	 
int Getdigit(int x,int y)
{
    
    
	int i,t=1;
	for(i=0;i<y;i++)
	{
    
    
		t*=10;
	}
	return (x%t)/(t/10);
}

void Radixsort(int arr[])
{
    
    
	int i,j,k,digit;
	List bucket[B];
	for(i=0;i<S;i++)
	{
    
    
		for(j=0;j<B;j++)
		{
    
    
			bucket[j]=MakeEmpty(bucket[j]);
		}
		for(j=0;j<N;j++)
		{
    
    
			digit = Getdigit(arr[j],i+1);
			InsertBack(arr[j],bucket[digit]);
			//cout<<"tag1 and digit and arr"<<j<<" "<<digit<<" "<<arr[j]<<endl;
		}
	//	for(j=0;j<B;j++)
	//			cout<<"bucket"<<j<<" "<<bucket[j]->num<<endl;
		k=0;
		for(j=0;j<B;j++)
		{
    
    
			Position temp = bucket[j];
			while(temp->next!=nullptr)
			{
    
    
				temp = temp->next;
				arr[k++]=temp->num;
			//	cout<<"tag2 and arr"<<k-1<<" "<<arr[k-1]<<endl;
			}
		}
		for(j=0;j<B;j++)
		{
    
    
			DeleteList(bucket[j]);
			//cout<<"tag3\n";
		}
		
	}
}

void printarr(int arr[],int n)
{
    
    
	for(int i =0;i<n;i++)
		cout<<arr[i]<<" ";
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/ZmJ6666/article/details/108663551