【链表】|单链表|双链表|模拟链表|链表的操作

一,链表

链表是最基础的线性的数据结构,通常情况下我们使用这种数据结构用来完成一些数组完成不了或者说完成的不是很好的操作,最主要的就是随机插入操作,无论是数组还是队列,栈操作,都不能很好的完成数值的插入的操作,因为数值的插入他们需要完全的改变下标值来完成,数值的插入都是需要进行一个 O(n) 的循环,但是对于链表来说他只需要 一个 O(1) 的时间,链表的插入操作 毫无疑问就是链表最核心的操作。

链表的存储结构,我们把链表使用数组的方式进行模拟,首先我们肯定会有一个 e[N] **idx ** 来存储链表的数据,idxe[N] 服务。然后就是链表的指向指针了,首先是单链表一定会有一个指针指向的是 下一个结点,同时他还会有配套的 head 存储头结点 ,双链表中的指针就是一个左指针和右指针 ,这两个指针中的头尾结点分别有特定的方式表示 。
在这里插入图片描述

二,单链表

1,单链表的结构

单链表是有两个部分组成的,一部分是数据存储部分 **e[N] idx ** ,也就是链表所谓的结点;另一部分就是指针操作,**ne[N] head ** ,这个就是链表中的链子;单链表的操作可以看成链子和结点的操作。

单链表的指针是 先进位的,每次停留的点都是没进行赋值的 ;


const int N = 100010 ;

int e[N], idx = 0 ;

int ne[N] , head ;

2,单链表的操作

1.单链表的初始化操作

//每次初始化的时候更改两个指针就行,数据存储的时候会覆盖原来数据

void init()
{
    
    
	head = - 1 ;
	idx = 0 ;
}
2.单链表的插入操作
对头的插入操作

//首先我们先开出一个结点,然后我们要给这个结点赋值,然后我们改变一些这个点的指针,再改变头结点的指针

void insert_head(int x)
{
    
    
	e[idx] = x , ne[idx] = head , head = idx ++ ;
}

对第几个数进行插入操作

//首先我们根据上面可以知道,我们要知道第**i - 1 ** 项的指针,指向的的 ne[i],求解这个 ** u ** ,把这个 u 当成 head 就可以了。

void insert(int x ,int j)
{
    
    
	int u ;
	for(u = head , int i = 0 ; i < j - 1; i ++ )
		u = ne[u] ;
	e[idx] = x, ne[idx] = u , u = idx ++ ;

}
3, 删除操作

//首先还是最简单的删除头节点的操作
我们直接对这个头进行赋值就行

void remove_head ()
{
    
    
	head = ne[head] ;
}

由于在链表中我们调用数据的时候不像数组那么灵活,所以我们一般情况下只调用头节点,在头节点完成插入,删除操作,
冷知识: 在单链表中随机插入的时间复杂度跟其他数据结构也差不多

链表的另一个有点,可以对第几个插入的数值进行查找,对 n 后面的数值进行删除

3,单链表总操作

#include <iostream>
using namespace std ;

const int N = 100010 ;

int idx = 0  , e[N] , ne[N] ,head = - 1 ;

void insert ( int  u)
{
    
    
	e[idx] = u , ne[idx] = head , head = idx ++ ;
}

void remove (int u )
{
    
    
	ne[u] = ne[ne[u]] ;
}


void add(int k, int x)
{
    
    
    e[idx] = x, ne[idx] = ne[k], ne[k] = idx ++ ;
}


int main ()
{
    
    
	int n ;
	cin >> n ;
	while ( n -- )
	{
    
    
		char s;
		int a , b ;
		cin >> s ;
		if(s == 'H')
		{
    
    
			cin >> a ;
			insert(a) ;
		}
		else if(s == 'D')
		{
    
    
			cin >> a ;
			if(a)
			remove(a - 1) ;
			//删除第 a 个插入的数的后面的数 
			else 
			{
    
    
			    head = ne[head] ;
			}
		}
		else 
		{
    
    
			cin >> a >> b ;
			add( a - 1, b );
			//注意:我们要完成的插入操作是第k位 后面的,当我们插入的时候 idx 是从0 开始的 所以我们减去 1 
		}	
	}

    for(int  i = head ; i != -1 ; i = ne[i] )
        cout << e[i] << " " ;
    return 0 ;
	return 0 ;
}

4,单链表的实际应用,来存储树

单链表存储树,首先树是由结点,边来组成的,所以我们执行需要了解两个操作,结点的创建,边的创建;

需要的变量

根节点 :h[N] // 有多个头所以我们下使用 h[N] 来代之
边结点 :e[N] ,ne[N] ,idx

创建的头结点

首先头节点要进行一次初始化,让他们都指向 - 1 ;

添加边

把所谓的 h[N]等价于 head 就可以了

void add (int a ,int b )
{
    
    
	e[ ++ idx] = b ;
	ne[idx] = h[a] ;
	h[a] = idx ;
}
``
### 3,双链表的实现
 
1,双链表的结构,双链表有两个指针 **l[N] r[N]** 和一个存储数值的地址 **idx ,e[N]**
  2,双链表的基本操作
###### 双链表的创建
  
```cpp
void init ()
{
    
    
	idx = 2 ;
	r[1] = 0 ;
	l[0] = 1 ;
}
双链表的插入操作(对第几位被插入的结点插入)

我们这个操作是插入第 k - 1个插入的数

void insert (int k ,int x)
{
    
    
    e[idx] = x ;
    l[idx] = k ;
    r[idx] = r[k] ;
    l[r[k]] = idx ;
    r[k] = idx ++ ;
}

完成像左边的插入操作就是让向左的指针走一次,再使用这个向后插入的函数,就能完成插入操作。

双链表的删除操作

双链表的删除就是使两边相互等于的操作

//删除第 k - 1 个插入的值 
void remove ( int k) 
{
    
    	
	l[r[k]] = l[k] ;
	r[l[k]] = r[k] ;
}

3,总的代码实现

#include <iostream>

using namespace std ;

const int N = 100010 ;

int idx , l[N] , r[N] , e[N] ;

void init ()
{
    
    
    r[0] = 1 ;
    l[1] = 0 ;
    idx = 2;
}
void insert (int k ,int x)
{
    
    
    e[idx] = x ;
    l[idx] = k ;
    r[idx] = r[k] ;
    l[r[k]] = idx ;
    r[k] = idx ++ ;
}
void remove (int k )
{
    
    
    r[l[k]] =r[k] ;
    l[r[k]] = l[k] ;
}

int main ()
{
    
    
    init();
    int n ;
    cin >> n ;
    while (n -- )
    {
    
    
        string str ;
        cin >> str ;
        int k , x;
        if(str == "L" ) 
        {
    
    
            cin >> x ;
            insert(0 , x) ;
        }   
        else if(str == "R")
        {
    
    
            cin >> x ;
            insert(l[1] , x );
        }
        else if (str == "D")
        {
    
    
            cin >> x ;
            remove(x + 1 );
        }
        else if (str == "IL")
        {
    
    
            cin >> k >> x ;
            insert(l[k + 1 ] , x );
        }
        else 
        {
    
    
            cin >> k >> x ;
            insert(k + 1 , x );
        }
    }
    for(int i = r[0] ; i != 1 ;i = r[i])
        cout <<e[i]<< " ";
    return 0 ;
}

3,循环链表操作

1,循环链表的构建

#include <iostream>
#include <algorithm>
using namespace std ;

const int N = 100010  ;

int ne[N] , e[N] ,idx ;


int main ()
{
    
    

   ____链表的构建_____
   //构建链表的n - 1个序列类似
   //  324 34  454 65 45665 6
   //   1   2   3   4  5    —    指针
   //   0   1   2   3  4    5    下标

   //构建链表的循环(n)  ne[6] = 0 ; 


   return 0 ;
}

2,循环链表的其他操作和单链表相同

例题 约瑟夫问题
n 个人围成一圈,从第一个人开始报数,数到 mm 的人出列,再由下一个人重新从 11 开始报数,数到 mm 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。

#include <iostream>
using namespace std ;

const int N = 120 ;

int e[N] ,ne[N] ,idx  ;

int main () 
{
    
    
    int n , m ;
    cin >> n >> m ;
    for(int i = 1 ; i < n ; i  ++ )
    {
    
    
        e[ ++ idx] = i ; 
        ne[idx] = idx + 1 ;
    }
    e[++ idx] = n ; 
    int p = 0  ;
    ne[0] = 1 ;
    ne[idx] = 1 ;
    for(int i = 0 ; i < n ; i ++ )
    {
    
    
        for(int j = 0 ; j < m - 1 ; j ++ )
            p = ne[p] ; 
        cout << e[ne[p]] << " ";
        ne[p] = ne[ne[p]]; 
    }
    return 0 ;
}

猜你喜欢

转载自blog.csdn.net/wen030803/article/details/131791164