线性表note

1.特性

在这里插入图片描述

2.顺序表

在这里插入图片描述

3.链表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.链表(插入)

在这里插入图片描述

5.链表(删除)

在这里插入图片描述

6.双向链表

在这里插入图片描述

7.双向链表(插入)

在这里插入图片描述

8.双向链表(删除)

在这里插入图片描述

9.循环链表

在这里插入图片描述

10.静态链表

在这里插入图片描述
上图的含义:data[ ]中存的是数据,right[ ]中存的是data[ ]中每一个数据的下一个数据的索引。比如data[ ]中第一个数据是空的,下一个数据56的索引是1,则right[ ]中第一个数据就是1。

静态链表的好处:假设一个大小1000的链表,若使用动态链表,寻找一个数据时,总是需要从头开始搜索,比较耗时,若使用静态链表,直接一步到位,按索引就找到该数据。

11.静态链表(插入)

在这里插入图片描述
在这里插入图片描述
若想在data[ ]链表中,数据49和数据20之间插入数据25:
方法:先将数据25放在data[ ]链表的最后,索引是9,将其插入在49和20中间,即将索引9(数据25)插入在索引5(数据49)和索引6(数据20)之间。
即索引5的下一个节点是9,索引9的下一个节点是6。
则在right[ ]链表中,索引5下面填9,索引9下面填6。

12.静态链表(删除)

在这里插入图片描述
在这里插入图片描述
若想在data[ ]链表中,删除数据12:
方法:在data[ ]链表中,数据12的索引是3,数据9的索引是2,数据23的索引是4,即直接将数据9和数据23相连,即索引2和索引4相连。
则在right[ ]链表中,在索引2处下面填4。
但不能删除数据12的空间,因为静态链表是一块连续的空间,所以数据12还存在该链表中,但索引已经跳过了它。

13.双向静态链表

在这里插入图片描述
在这里插入图片描述

14.双向静态链表(插入)

在这里插入图片描述
在这里插入图片描述

15.双向静态链表(删除)

在这里插入图片描述
在这里插入图片描述

练习

01

题目:https://www.luogu.com.cn/problem/UVA101
题解:
在这里插入图片描述
在这里插入图片描述
归位:
在这里插入图片描述
移动:
在这里插入图片描述

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

vector<int> block[30]; //arr数组中有30个元素,每个元素又是一个vector<int>类型的数组
int n; //初始时从左到右有n个木块

//初始化输入的n个容器
void Init()
{
    
    
	cin >> n;
	for (int i = 0; i < n; i++)
	{
    
    
		block[i].push_back(i);//arr[i]表示横向的索引;push_back(i)表示初始时的方块
	}
}

void loc(int x, int& p, int& h)//穷举找位置,x表示要找的数,h表示高度,p表示堆数
{
    
    
    for (int i = 0; i < n; i++)
        for (int j = 0; j < block[i].size(); j++)
        {
    
    
            if (block[i][j] == x) 
            {
    
    
                p = i;
                h = j;
            }
        }
}

void goback(int p, int h)//p堆 > h的所有块归位
{
    
    
    for (int i = h + 1; i < block[p].size(); i++)
    {
    
    
        int k = block[p][i];
        block[k].push_back(k); //归位
    }
    block[p].resize(h + 1);//重置大小
}

void moveall(int p, int h, int q)//p堆 >=h 的所有块移动到q之上
{
    
    
    for (int i = h; i < block[p].size(); i++)
    {
    
    
        int k = block[p][i];
        block[q].push_back(k);
    }
    block[p].resize(h);//重置大小
}

//接收指令
void solve()
{
    
    
    int a, b;
    string s1, s2;
    while (cin >> s1)
    {
    
    
        if (s1 == "quit")
            break;
        cin >> a >> s2 >> b;
        int ap = 0, ah = 0, bp = 0, bh = 0;
        loc(a, ap, ah); //找到a位置
        loc(b, bp, bh); //找到b位置
        if (ap == bp) //如果有非法指令(如 aa 与 bb 在同一堆),无需处理
            continue;
        if (s1 == "move")//a归位
            goback(ap, ah);
        if (s2 == "onto")//b归位
            goback(bp, bh);
        moveall(ap, ah, bp);
    }
}

void print()
{
    
    
    for (int i = 0; i < n; i++)
    {
    
    
        cout << i << ":";
        for (int j = 0; j < block[i].size(); j++)
            cout << " " << block[i][j];
        cout << endl;
    }
}

int main()
{
    
    
    Init(); // 初始化
    solve(); // 输入指令
    print(); //打印结果
    return 0;
}

02

题目:https://www.luogu.com.cn/problem/UVA11988

题解:输入:This_is_a_[Beiju]_text
当打字This_is_a_之后,遇到 '[',说明按下了home键,即光标跑到行首,
在行首输入Beiju,此时悲剧文本为BeijuThis_is_a_,又遇到 ']',说明按下
了end键,即光标跑到行尾,在行尾接着输入_text,此时悲剧文本为BeijuThis_is_a__text。

eg.

#include <iostream>
#include <list>
//list将元素按顺序储存在链表中. 与 向量(vectors)相比, 它允许快速的插入和删除,但是随机访问却比较慢
//https://blog.csdn.net/wuruiaoxue/article/details/48159973
//https://www.cnblogs.com/denggelin/p/5745213.html
using namespace std;

void solve(string s)
{
    
    
	int len = s.size();
	list<char> text;
	list<char> ::iterator it = text.begin();
	for (int i = 0; i < len; i++)
	{
    
    
		if (s[i] == '[') {
    
    
			it = text.begin();
		}
		else if (s[i] == ']') {
    
    
			it = text.end();
		}
		else {
    
    
			it = text.insert(it, s[i]);
			it++;
		}
	}

	for (it = text.begin(); it != text.end(); it++)
	{
    
    
		cout << *it;
	}
	s.clear();
	cout << endl;
}

int main(void)
{
    
    
	string s;
	while (cin >> s)
	{
    
    
		solve(s);
	}
	return 0;
}

03

题目:https://www.luogu.com.cn/problem/UVA12657
题解:
在这里插入图片描述
程序思路:如果元素个数很大的话,不要真的进行反转,否则时间复杂度很高。所以对于题目中的指令4,将整条线反转,可以进行转化:对于指令1,进行反转后,就变成了指令2;对于指令2,进行反转后,就变成了指令1。

#include <iostream>
using namespace std;

int left_array[100000], right_array[100000]; //声明前驱、后继数组

//初始化双向静态链表,根据笔记双向链表的部分,前驱、后继数组
void initDoubleLink(int num)
{
    
    
	//前驱left数组索引全部减1,补上一个left[0] = n,后继right数组索引全部加1,
	//因为原数组索引是1至n, 加1后变为2至n+1,所以补上一个0和1,即right[num] = 0, right[0] = 1
	for (int i = 1; i <= num; i++)
	{
    
    
		left_array[i] = i - 1;
		right_array[i] = i + 1;
	}
	left_array[0] = num;
	right_array[num] = 0;
	right_array[0] = 1;
}

//链接的操作
//比如1,2,3;1表示L,3表示R。若删除2,则将1和2链接,即L的后继是R,R的前驱是L。
void link(int L, int R)
{
    
    
	left_array[R] = L;
	right_array[L] = R;
}

int main(void)
{
    
    
	int k = 0; //输出格式Case 1,Case 2...
	int n_num, m_order; //盒子个数,指令个数
	int index_order, x_box, y_box; //指令类型,盒子编号x,盒子编号y
	bool overturn_flag; //是否反转标识位

	while (cin >> n_num >> m_order)
	{
    
    
		overturn_flag = false;
		initDoubleLink(n_num);
		for (int i = 0; i < m_order; i++)
		{
    
    
			cin >> index_order;
			if (index_order == 4)
			{
    
    
				overturn_flag = !overturn_flag;//反转标识位翻转
			}
			else 
			{
    
    
				cin >> x_box >> y_box;

				//交换盒子编号X与盒子编号Y的位置,考虑X在Y的右边
				//如果指令类型是3,且Y的后继是X,则直接交换Y和X的值
				if (index_order == 3 && right_array[y_box] == x_box)
					swap(x_box, y_box);
				//上面已经对指令类型3和4进行了处理,这里只剩下了1和2指令,
				//若反转标识也为true,则指令1将X放到Y左边经过反转后变为将X放到Y右边,即指令1变成了指令2
				if (index_order != 3 && overturn_flag)
					index_order = 3 - index_order;
				//如果X已经在Y的左边了就忽略
				if (index_order == 1 && x_box == left_array[y_box])
					continue;
				//如果X已经在Y的右边了就忽略
				if (index_order == 2 && x_box == right_array[y_box])
					continue;

				//指令1和指令2的操作
				int L_x_box = left_array[x_box], R_x_box = right_array[x_box]; //盒子编号x的前驱和后继
				int L_y_box = left_array[y_box], R_y_box = right_array[y_box]; //盒子编号x的前驱和后继
				if (index_order == 1)
				{
    
    
					//移动盒子编号X到盒子编号Y的左边
					link(L_x_box, R_x_box); //将X的前驱和后继直接相链接,删除X
					//Y的前驱与X相链接,X与Y相链接,完成X移动到Y的左边
					link(L_y_box, x_box); 
					link(x_box, y_box);
				}
				else if (index_order == 2)
				{
    
    
					//移动盒子编号X到盒子编号Y的右边
					link(L_x_box, R_x_box);
					link(y_box, x_box);
					link(x_box, R_y_box);
				}
				else if (index_order == 3) //交换盒子编号X与盒子编号Y的位置,保证X在Y的左边
				{
    
    
					//如果X和Y靠一起,X的后继是Y
					if (right_array[x_box] == y_box)
					{
    
    
						link(L_x_box, y_box);
						link(y_box, x_box);
						link(x_box, R_y_box);
					}
					else //如果X和Y不靠在一起
					{
    
    
						link(L_x_box, y_box);
						link(y_box, R_x_box);
						link(L_y_box, x_box);
						link(x_box, R_y_box);
					}
				}

			}
		}

		//对于每组数据,输出他们奇数位置的编号的和
		//若盒子的总数是奇数,比如1,2,3,4,5,6,7;可以发现无论是正向还是反转,1357、7531,奇数位置的编号和都是相等的
		//若盒子的总数是偶数,比如1,2,3,4,5,6;若是正向,则是奇数位置编号的和(135),若是反转的,奇数位置编号的和(642) = 总和 - 正向的奇数编号和(135)
		long long odd_sum = 0; //奇数总和
		int t = 0;

		//若盒子总个数为奇数
		for (int i = 1; i <= n_num; i++)
		{
    
    
			t = right_array[t];
			if (i % 2 == 1)
				odd_sum += t;
		}
		//若盒子总个数为偶数
		if (overturn_flag && n_num % 2 == 0)
		{
    
    
			odd_sum = (long long)n_num * (n_num + 1) / 2 - odd_sum;
		}
		cout << "Case " << ++k << ":" << odd_sum << endl;
	}
	return 0;
}

Guess you like

Origin blog.csdn.net/qq_45445740/article/details/111653435