- 线性结构
- 非线性结构
- 稀疏sparsearray数组
- 队列
- 环形队列
- 链表Linked List
- 双向链表
线性结构
(1)线性结构作为常用的数据结构,其特点是数据元素之间存在一对一的线性关系
(2)线性结构两种不同的存储结构,即顺序存储结构(数组)和链式存储结构(链表)。顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的
(3)链式存储的线性表成为链表,链表中的存储元素不一定是连续的,元素结点中存放数据元素以及相邻元素的地址信息
(4)线性结构常见的有:数组,队列,链表和栈
非线性结构
非线性结构包括:二维数组,多维数组,广义表,树结构,图结构
稀疏sparsearray数组
适用场景:适用二维数组记录棋盘,可以对数据进行压缩
基本介绍:但一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存数组。
稀疏数组的处理方法时:
(1)记录数组一共有几行几列,有多少个不同的值
(2)把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模
二维数组转稀疏数组的思路: 1,遍历袁术的二维数组,得到有效数据的个数count
2,根据count就可以创建稀疏数组sparseArr
int [count+1][3]
3,将二维数组的有效数据存入到稀疏数组
稀疏数组转原始的二维数组思路:
(1)先读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组
(2)在读取稀疏数组后几行的数据,并赋给原始的二维数组即可
代码实现:
public static void main(String[] args) {
//创建一个原始的二维数组 11*11
//0表示没有棋子,1表示黑子,2表示白子
int chessArr1[][] = new int [11][11];
chessArr1[1][2] = 1;
chessArr1[2][3] = 2;
//输出原始的二维数组
System.out.println("原始二维数组");
for(int[] row : chessArr1) {
for(int data:row) {
System.out.printf("%d\t", data);
}
System.out.println();
}
//将二维数组转稀疏数组
//先遍历二维数组得到非0数据的个数
int count = 0;
for(int i = 0;i < 11;i++) {
for(int j = 0;j < 11;j++) {
if(chessArr1[i][j] != 0)
count++;
}
}
//创建对应的稀疏数组
int sparseArr[][] = new int[count + 1][3];
//给稀疏数组赋值
sparseArr[0][0] = 11;
sparseArr[0][1] = 11;
sparseArr[0][2] = count;
//遍历二维数组,将非0的值存放发到spaseArr中
count = 0;
for(int i = 0;i < 11;i++){
for(int j = 0;j < 11;j++) {
if(chessArr1[i][j] != 0) {
count++;
sparseArr[count][0] = i;
sparseArr[count][1] = j;
sparseArr[count][2] = chessArr1[i][j];
}
}
队列
队列是一个有序列表,可以用数组或是链表来实现
遵循先进先出的原则
代码实现:
class QueueArr{
private int MaxSize;
private int front;
private int rear;
private int[] arr;
//创建一个队列
public QueueArr(int max) {
MaxSize = max;
arr = new int[MaxSize];
front = -1;
rear = -1;
}
//判断队列是否为空
public boolean ISEmpty(){
return front == rear;
}
//判断队列是否已满
public boolean IsFull() {
return rear == MaxSize-1;
}
//出队
public int GetQueue() {
if(ISEmpty()) {
throw new RuntimeException("队列是空的");
}
front++;
return arr[front];
}
//入队
public void AddQueue(int n) {
if(IsFull()) {
System.out.println("队列已经满了");
return ;
}
rear++;
arr[rear] = n;
}
//返回队头元素
public int getHead() {
if(ISEmpty()) {
throw new RuntimeException("队列是空的");
}
return arr[front+1];
}
//显示对列里的所有数据
public void ShowQueue() {
if(ISEmpty()){
System.out.println("队列是空的");
return ;
}
for(int i = front+1;i <= rear;i++) {
System.out.printf("arr[%d] = %d\n",i,arr[i]);
}
}
环形队列
思路:
(1)front变量的含义做一个调整:front就指向队列的第一个元素,也就是说arr[front]就是队列的第一个元素front的初始值=0;
(2)rear变量的含义做一个调整:rear指向队列的最后一个元素的后一个位置,因为希望空出一个空间作为约定,rear的初始值=0;
(3)当队列满时,条件是(rear + 1)%MaxSize = =front【满】 (4)对列为空的条件,rear== front空
(5)当我们这样分析,队列中有效的数据的个数(rear+MaxSize-front)%MaxSize
代码示例:
class CQueue{
private int MaxSize;
private int front;
private int rear;
private int[] arr;
//初始化环形队列
public CQueue(int m) {
MaxSize = m;
arr = new int[MaxSize];
front = 0;
rear = 0;
}
//计算环形队列的大小
public int Size() {
return (rear + MaxSize - front)%MaxSize;
}
//判断环形队列是否为空
public boolean IsEmpty() {
return rear == front;
}
//判断环形队列是否为满
public boolean IsFull() {
return (rear + 1)%MaxSize == front;
}
//环形对列进队
public void AddCQueue(int n) {
if(IsFull()) {
System.out.println("环形队列已经满了");
return ;
}
arr[rear] = n;
rear++;
if(rear > MaxSize) {
}
return ;
}
//环形队列出队
public int GetCQueue() {
if(IsEmpty()) {
throw new RuntimeException("环形队列是空的");
}
int value = arr[front];
front = (front+1)%MaxSize;
return value;
}
//输出整个环形队列
public void ShowCQueue() {
if(IsEmpty()) {
System.out.println("环形队列是空的");
return;
}
int j = front;
for(int i = front;i < front + Size();i++) {
System.out.printf("arr[%d] = %d\n",i%MaxSize,arr[i%MaxSize]);
}
}
//打印环形数组的首元素
public void HeadCQueue() {
if(IsEmpty()){
System.out.println("环形队列为空");
}
System.out.println(arr[front]);
}
链表(Linked List)
(1)链表是以结点的方式来存储
(2)每个结点包含data域:储存数据,next域:指向下一个结点
(3)链表分带头结点的链表和没有头结点的链表
代码示例:
class nodeList{
//创建头结点
Node head = new Node(0);
//向链表中添加元素
public void AddNode(Node node) {
//先将指针指到头结点
Node temp = head;
//在顺着链表找到链表的尾部进行元素添加
while(true) {
if(temp.next == null) {
break;
}
//指针指向下一个结点
temp = temp.next;
}
//将插入元素插到列表末尾
temp.next = node;
node.next = null;
}
//删除指定data的节点
public void delete(int data) {
//判断链表是否为空
if(this.head.next == null) {
System.out.println("链表是空的,无法进行删除操作");
return ;
}
Node temp = head;
//判断链表是否找到相应的删除节点
boolean flag = false;
while(true) {
if(temp.next == null) {
System.out.println("删除操作失败,没有找到相应的结点");
break;
}
if(temp.next.data == data) {
flag = true;
break;
}
temp = temp.next;
}
if(flag) {
temp.next = temp.next.next;
}
}
//链表翻转
/*
思路:
遍历原先链表,将遍历的链表元素插入到临沂个新链表的最前面,完成翻转
*/
public void reverse() {
//判断链表为空或者只有一个结点(除了头结点)则直接返回,不用翻转
if(head.next == null || head.next.next == null) {
return ;
}
//定义一个新的头结点
Node rHead = new Node(0);
//定义一个循环原始链表的指针,将其初始值定义为原来链表的head.next
Node cur = head.next;
//定义一个指针指向当前结点的下一个结点
Node next = cur.next;
//遍历链表
while(true) {
//先记录下新链表的头结点的后一个结点
Node temp = rHead.next;
//将结点插入到新链表的最前面
rHead.next = cur;
//将原来链表cur后面的结点记录下来
next = cur.next;
//将插入后的结点的next置为原来rHead的next
cur.next = temp;
//判断如果当前结点的下一个结点为空,没么就已经遍历完整个链表了
if(next == null) {
break;
}
//将指针指向链表的下一个结点
cur = next;
//同时将指针后移
next = cur.next;
}
//将元来的链表头指向现在的链表,完成链表翻转
head.next = rHead.next;
}
//更改链表中相应位置元素的值
public void upData(int formData, int newData) {
//判断链表是否为空
if(this.head.next == null) {
System.out.println("链表是空的,无法更新数据");
return;
}
//定义指针元素,进行遍历
Node temp = head;
//定义flag判断是否村在相应的要找的位置
boolean flag = false;
while(true) {
if(temp.data == formData) {
break;
}
if(temp.next == null) {
flag = true;
return;
}
temp = temp.next;
}
//如果要找的位置不存在,那么直接return
if(flag) {
System.out.println("更新失败,没有找到更新的内容");
return ;
}
temp.data = newData;
}
//将元素插入到指定data(n)的后面
public void AddNodeSite(Node node ,int n) {
Node temp = head;
//设置一个布尔值,来判断是否找到相应插入的位置
boolean flag = false;
//遍历链表,找到相应的位置
while(true) {
//找到相应位置,直接break
if(temp.data == n) {
break;
}
//没找到相应的位置,改变flag的值并且跳出循环
if(temp.next == null) {
flag = true;
break;
}
temp = temp.next;
}
//如果没找到相应插入的位置则插入失败
if(flag) {
System.out.println("插入失败,没有找到合适的插入位置");
return ;
}
//找到插入位置后,将前后结点都记录下来
Node tempNext = temp.next;
//将前置结点元素的next变为新的结点元素
temp.next = node;
//如果找到的位置是在链表的末尾,那么就将查睿的结点的next设置为null
if(temp.next != null) {
node.next = null;
}
else {
node.next = tempNext;
}
}
//输出链表
public void list() {
Node temp = head;
//判断链表是否为空
if(head.next == null) {
System.out.println("链表是空的");
return ;
}
temp = head.next;
//一直循环输出,直到输出了最后一个元素为止
while(true) {
//因为在while循坏之外就已经将temp指向head后的元素,所以要先打印在判断
System.out.println(temp);
if(temp.next == null) {
break;
}
//将指针指向下一个元素
temp = temp.next;
}
}
}
//链表结点
class Node{
public int data;
public Node next;
public Node(int data) {
this.data = data;
}
//重写toString方法
public String toString() {
return "Node [data=" + data + "]";
}
双向链表
(1)单向链表,查找的反向只能是一个反向,二双链表可以向前,向后查找
(2)单向量表不能自我伤处,只能靠辅助,二双向链表,则可以自我删除
代码实现:
class doubleListedList {
//创建头结点
DNode head = new DNode(0);
//向链表中添加元素
public void AddDNode(DNode node) {
//先将指针指到头结点
DNode temp = head;
//在顺着链表找到链表的尾部进行元素添加
while(true) {
if(temp.next == null) {
break;
}
//指针指向下一个结点
temp = temp.next;
}
//将插入元素插到列表末尾
temp.next = node;
node.per = temp;
node.next = null;
}
//删除指定data的节点
public void delete(int data) {
//判断链表是否为空
if(this.head.next == null) {
System.out.println("双向链表是空的,无法进行删除操作");
return ;
}
DNode temp = head.next;
//判断链表是否找到相应的删除节点
boolean flag = false;
while(true) {
if(temp == null) {
System.out.println("删除操作失败,没有找到相应的结点");
break;
}
if(temp.data == data) {
flag = true;
break;
}
temp = temp.next;
}
//找到相应删除的位置
if(flag) {
//将temp前置结点的next改成temp结点的next
temp.per.next = temp.next;
//如果temp的及诶单不是最后一个结点,那么就把temp后置结点的per改为temp的前置结点
if(temp.next != null) {
temp.next.per = temp.per;
}
}
}
//输出链表
public void list() {
DNode temp = head;
//判断链表是否为空
if(head.next == null) {
System.out.println("链表是空的");
return ;
}
temp = head.next;
//一直循环输出,直到输出了最后一个元素为止
while(true) {
//因为在while循坏之外就已经将temp指向head后的元素,所以要先打印在判断
System.out.println(temp);
if(temp.next == null) {
break;
}
//将指针指向下一个元素
temp = temp.next;
}
}
}
//链表结点
class DNode{
//存储数据的data
public int data;
//指向下一个结点的指针
public DNode next;
//指向上一个结点的指针
public DNode per;
public DNode(int data) {
this.data = data;
}
//重写toString方法
public String toString() {
return "DNode [data=" + data + "]";
}
}