引子:数据结构分为线下结构和非线性结构
数据结构的分类
(一)线性结构
1.数据元素之间存在一对一的线性关系。
2.线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构。顺序存储的线性表称为顺序表,顺序表中的存储结构是连续的。
3.链式存储结构的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息。
4.线性结构常见的有:数组,队列,链表和栈。
(二)非线性结构
非线性结构包括:二维数组,多维数组,广义表,树结构,图结构。
稀疏数组
当一个数组中大部分元素为0,或者为同一个值的数组时,可以用稀疏数组来保存该数组。
思路:先记录数组有几行几列,以及用sum变量记录有不用的值(当然这里前提已知了很多相同元素的值为多少) ,再把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模。
例题:五子棋程序中,有存盘退出和续上盘的功能。
代码如下(具体细节部分 看注释 Java代码)
package com.jxust.sparseArray;
public class SparesArray {
public static void main(String[] args) {
//创建二维数组 11*11
// 0 代表没有棋子,1表示黑子 2 表示白子
int[][] arr1 = new int[11][11];
arr1[1][2] = 1;
arr1[2][3] = 2;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++)
System.out.printf("%d \t", arr1[i][j]);
System.out.println();
}
//数组有多少个非0数
int sum = 0;
for (int i = 0; i < 11; i++)
for (int j = 0; j < 11; j++) {
if (arr1[i][j] != 0) sum++;
}
//赋值稀疏数组
int cnt = 0;
int[][] arr2 = new int[sum + 1][3];
arr2[0][0] = 11;
arr2[0][1] = 11;
arr2[0][2] = sum;
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++) {
if (arr1[i][j] != 0) {
cnt++;
arr2[cnt][0] = i;
arr2[cnt][1] = j;
arr2[cnt][2] = arr1[i][j];
}
}
}
//输出稀疏数组
System.out.println("==========");
for (int i = 0; i <= sum; i++) {
for (int j = 0; j < 3; j++) {
System.out.printf("%d ", arr2[i][j]);
}
System.out.println();
}
//恢复原始数组
System.out.println("=================");
int[][] arr3=new int[ arr2[0][0] ][ arr2[0][1] ];
for (int i=1; i<arr2.length;i++)
arr3[ arr2[i][0] ][ arr2[i][1]]=arr2[i][2];
for (int i = 0; i < 11; i++) {
for (int j = 0; j < 11; j++)
System.out.printf("%d \t", arr1[i][j]);
System.out.println();
}
}
}
单链表
定义:每个节点只含有一个指针域的链式结构称为单链表 (下面用数组进行模拟 C++代码)
1. 单链表
// head存储链表头,e[]存储节点的值,ne[]存储节点的next指针,idx表示当前用到了哪个节点 就相当于用数组代替指针执行操作
int head, e[N], ne[N], idx;// 初始化
void init()
{
head = -1;
idx = 0;
}// 在链表头插入一个数a
void insert(int a)
{
e[idx] = a, ne[idx] = head, head = idx ++ ;
}// 将头结点删除,需要保证头结点存在
void remove()
{
head = ne[head];
}
例题 (ACWing 826):
实现一个单链表,链表初始为空,支持三种操作:
- 向链表头插入一个数;
- 删除第 kk 个插入的数后面的数;
- 在第 kk 个插入的数后插入一个数。
现在要对该链表进行 MM 次操作,进行完所有操作后,从头到尾输出整个链表。
注意:题目中第 kk 个插入的数并不是指当前链表的第 kk 个数。例如操作过程中一共插入了 nn 个数,则按照插入的时间顺序,这 nn 个数依次为:第 11 个插入的数,第 22 个插入的数,…第 nn 个插入的数。
输入格式
第一行包含整数 MM,表示操作次数。
接下来 MM 行,每行包含一个操作命令,操作命令可能为以下几种:
H x
,表示向链表头插入一个数 xx。D k
,表示删除第 kk 个插入的数后面的数(当 kk 为 00 时,表示删除头结点)。I k x
,表示在第 kk 个插入的数后面插入一个数 xx(此操作中 kk 均大于 00)。输出格式
共一行,将整个链表从头到尾输出。
数据范围
1≤M≤1000001≤M≤100000
所有操作保证合法。输入样例:
10
H 9
I 1 1
D 1
D 0
H 6
I 3 6
I 4 5
I 4 5
I 3 4
D 6输出样例:
6 4 6 5
#include<iostream>
using namespace std;
const int N=100010;
int head,e[N],ne[N],idx;
//head 头结点的下标
//e[i]表示结点i的值
//ne[i]表示节点i的next指针是多少
//idx当前用到的点
//初始化
void init(){
head=-1;
idx=0;
}
//将x插入到头节点
void add_to_head(int x)
{
e[idx]=x;
ne[idx]=head;
head=idx;
idx++;
}
//将x插到k节点后面
void add(int k,int x){
e[idx]=x;
ne[idx]=ne[k];
ne[k]=idx;
idx++;
}
//删除k后面的点
void remove(int k){
ne[k]=ne[ne[k]];
}
int main(){
int m; cin>>m;
init();
while (m--){
int k,x; char c;
cin>>c;
if(c=='H'){
cin>>x;
add_to_head(x);
}
else if(c=='D'){
cin>>k;
if(!k) head=ne[head];
else remove(k-1);
}
else{
cin>>k>>x;
add(k-1,x);
}
}
for(int i=head;i!=-1;i=ne[i]) cout<<e[i]<<' ';
}
v