目录
1.逻辑结构和物理结构(存储结构)
1.1 逻辑结构
- 集合结构(无关系)
- 线性结构(一对一)
- 代表:线性表,栈,队列, 串
- 非线性结构
- 树形结构(一对多)
- 图形结构(多对多)
1.2 物理结构
- 顺序存储结构
- 链式存储结构
- 索引
- 散列表
2. 稀疏数组
2.1 介绍
- 当一个数组中有效值少,0很多的时候,可以用稀疏数组存储。SparseArray
- 如下所示,稀疏数组第0行,第一列6表示原数组有几行,第二列7表示原数组有几列,第三列8表示原数组有几个非0元素。稀疏数组第1行到最后一行的记录非0元素所在行列和它的值。
- 由此可见,稀疏数组的列必是3,行数就是原始数组的非0元素个数+1(第0行)。


2.2 代码实现
package SparseArray;
public class Demo01 {
public static void main(String[] args) {
// 1. 创建原始数组,只要给需要的地方赋值非0,其余默认0
int[][] o_array = new int[5][6];
o_array[1][3] = 1;
o_array[2][4] = 2;
o_array[0][3] = 5;
o_array[3][2] = 4;
// 2. 获取原始数组的行列
int lenx = o_array.length;
int leny = o_array[0].length;
// 3. 获取非0元素个数
int non_zero = 0;
for (int i =0; i<lenx;i++){
for (int j=0;j<leny;j++){
int temp = o_array[i][j];
if(temp!=0){
non_zero++;
}
}
}
// 4. 创建稀疏数组
int[][] sparse_array = new int[non_zero+1][3];
sparse_array[0][0] = lenx;
sparse_array[0][1] = leny;
sparse_array[0][2] = non_zero;
// 5. 遍历原始数组,把非0元素填入稀疏数组
int x = 1;
for (int i =0; i<lenx;i++){
for (int j=0;j<leny;j++){
int temp = o_array[i][j];
if(temp!=0){
sparse_array[x][0] = i;
sparse_array[x][1] = j;
sparse_array[x][2] = temp;
x++;
}
}
}
// 6. 输出原始数组
System.out.println("原始数组如下");
for (int[] row: o_array){
for (int data:row){
System.out.printf("%d\t", data);
}
System.out.println();
}
// 7. 输出稀疏数组
System.out.println("稀疏数组如下");
for (int[] row:sparse_array){
for (int data:row){
System.out.printf("%d\t", data);
}
System.out.println();
}
// 8. 把稀疏数组回复成原始数组
// 8.1 先获取稀疏数组第一行数据,用来创建原始数组大小
int lenx2 = sparse_array[0][0];
int leny2 = sparse_array[0][1];
int[][] array2 = new int[lenx2][leny2];
// 8.2 遍历稀疏数组,从第2行开始。把数据恢复
for (int i=1; i< sparse_array.length; i++){
array2[ sparse_array[i][0] ][ sparse_array[i][1] ] = sparse_array[i][2];
}
// 8.3 输出新的恢复后的原始数组
System.out.println("恢复后的原始数组如下");
for (int[] row: array2){
for (int data:row){
System.out.printf("%d\t", data);
}
System.out.println();
}
}
}
3 队列
3.1 介绍
- 队列是一个有序表,可以用数组或者链表实现
- 先入先出

3.2 代码实现
-
rear和front默认-1(添加和取数据的时候,先+1再添加),如果默认=0,那么操作过程就不一样了(添加时候,先添加,然后rear+1,取数据时,先取然后front+1)。
-
下面的队列是一次性的。因为队列填满值之后,就不能再加入了,因为填入的值没有出队列,只是返回了值而已。
public class Demo {
public static void main(String[] args) {
ArrayQueue a = new ArrayQueue(5);
a.addQueue(1);
a.addQueue(2);
a.addQueue(3);
a.addQueue(4);
a.addQueue(5);
a.showAllQueue();
System.out.println();
System.out.println(a.getQueue());
System.out.println(a.getQueue());
System.out.println(a.getQueue());
System.out.println();
a.showAllQueue();
System.out.println();
a.showQueue();
a.addQueue(6);
a.addQueue(7);
a.addQueue(8);
a.showAllQueue();
}
}
class ArrayQueue{
private int maxSize; // 队列最大容量
private int front; // 队列头指针
private int rear; // 队列尾指针
private int[] arr; // 数组,用来模拟队列,保存数据
// 构造器
public ArrayQueue(int maxSize){
this.maxSize = maxSize;
arr = new int[maxSize];
front = -1;
rear = -1;
}
// 判断队列是否满了,如果尾指针==最大值-1(尾指针0开始),表示满了
public boolean isFull(){
return rear==maxSize-1;
}
// 判断队列是否为空,如果头尾指针相同,就是空了
public boolean isEmpty(){
return front==rear;
}
// 添加数据到队列
public void addQueue(int n){
if (isFull()) {
System.out.println("队列已满,无法添加数据!");
return;
}
// arr[++rear] = n;
// 或者
rear ++;
arr[rear] = n;
}
// 获取队列数据,即从头部拿出数据
public int getQueue(){
if(isEmpty()){
// 不能return,因为return的值可能是队列元素的值
throw new RuntimeException("队列为空,不能取数据");
}
front++;
return arr[front];
}
// 打印队列未取出的内容
public void showQueue(){
if (isEmpty()){
System.out.println("队列为空,没有数据");
return;
}
for (int i=front+1; i<arr.length; i++){
System.out.printf("arr[%d] == %d\t", i, arr[i]);
}
}
// 打印队列所有的内容,就算有取出的数据,那也只是返回了,而arr中并没有删除!
public void showAllQueue(){
if (isEmpty()){
System.out.println("队列为空,没有数据");
return;
}
for (int i=0; i<arr.length; i++){
System.out.printf("arr[%d] == %d\t", i, arr[i]);
}
}
// 获取头部数据
public int getHead(){
if (isEmpty()) throw new RuntimeException("队列为空,无法获取头部数据");
return arr[++front];
}
}
4 环形队列
4.1 介绍
- 因为3中的队列是一次性的,所以构造环形队列,使队列可以重复利用!
- 环形队列具体内容参考《大话数据结构》p114-p117(非常重要)
- 之所以rear指向一个空的空间,是为了区分rear == front到底是空还是满!rear == front的时候为空,而(rear指向的空空间的索引 +1 )%maxSize后如果 == front,那就是满了

4.2 代码实现
package MyQueue;
public class CircleQueue {
private int maxSize; // 队列最大容量
private int front; // 队列头指针,默认0
private int rear; // 指向队列最后一个元素的后一个位置,且该位置只能为空,默认0
private int[] arr; // 数组,用来模拟队列,保存数据
// 构造器
public CircleQueue(int maxSize1){
this.maxSize = maxSize1+1;
arr = new int[maxSize];
front = 0;
rear = 0;
}
// 判断队列是否满了,如果尾指针==最大值-1(尾指针0开始),表示满了
public boolean isFull(){
return (rear+1)%maxSize == front;
}
// 判断队列是否为空,如果头尾指针相同,就是空了
public boolean isEmpty(){
return front==rear;
}
// 添加数据到队列
public void addQueue(int n){
if (isFull()) {
System.out.println("队列已满,无法添加数据!");
return;
}
arr[rear] = n;
rear = (rear+1)%maxSize;
}
// 获取队列数据,即从头部拿出数据
public int getQueue(){
if(isEmpty()){
// 不能return,因为return的值可能是队列元素的值
throw new RuntimeException("队列为空,不能取数据");
}
int res = arr[front];
front = (front+1)%maxSize;
return res;
}
// 打印队列所有的内容,就算有取出的数据,那也只是返回了,而arr中并没有删除!
public void showAllQueue(){
if (isEmpty()){
System.out.println("队列为空,没有数据");
return;
}
for (int i=front; i<front+getSize(); i++){
System.out.printf("arr[%d] == %d\t", i%maxSize, arr[i%maxSize]);
}
}
// 获取队列长度
public int getSize(){
return (rear - front + maxSize)%maxSize;
}
// 获取头部数据
public int getHead(){
if (isEmpty()) throw new RuntimeException("队列为空,无法获取头部数据");
return arr[front]; // front直接指向第一个元素,返回之后他不需要++,取队列需要++;
}
}
5 链表
5.1 参考大话数据结构
5.2 单链表
5.2.1 直接插入到尾节点单链表代码实例
package LinkedList;
public class SingleLinkedListDemo {
public static void main(String[] args) {
Node n1 = new Node(1,1);
Node n2 = new Node(2,2);
Node n3 = new Node(3,3);
SingleLinkedList s1 = new SingleLinkedList();
s1.addNode(n1);
s1.addNode(n2);
s1.addNode(n3);
s1.list();
// out message
// Node{num=1, data=1}
// Node{num=2, data=2}
// Node{num=3, data=3}
}
}
class SingleLinkedList {
// 头节点不能动
private Node head = new Node(0, 0);
// 添加一个新节点
public void addNode(Node n){
// temp拿到临时节点,开始遍历
Node temp = head;
// 找到最后一个节点,然后把新节点直接添加上去
while (true){
// 如果当前节点next=null,表示当前为尾节点,则跳出循环
if (temp.next == null){
break;
}
// 否则就看下一个节点
temp = temp.next;
}
// 添加节点
temp.next = n;
}
// 显示链表
public void list(){
// first to assert isEmpty
if (head.next == null){
System.out.println("the list is empty!");
return;
}
// head node we can not use, so make temp = head node.
// because if head node is used, it will point other value
Node temp = head.next;
// 判断最后一个节点是否为空,为空则表示当前节点为最后一个节点,直接推出循环。
// 不为空就输出节点信息,并判断下一个。
while (true){
if (temp == null){
break;
}
System.out.println(temp);
temp = temp.next;
}
}
}
class Node{
public int num; // 默认0
public int data; // 默认0
public Node next; // 默认null
public Node(int num, int data){
this.num = num;
this.data = data;
}
// 没有+next,不然就套娃了
@Override
public String toString() {
return "Node{" +
"num=" + num +
", data=" + data +
'}';
}
}
5.2.2 带排序的单链表代码实现
- 根据num进行节点排序插入,从小到大
- 先找到插入的位置,temp节点,必须是插入位置的前一个节点。
- 只需要添加一个addbynum方法即可。
// 根据节点num从小到大排序
public void addNodeSortByNum(Node n){
// 不能修改head结点,所以使用temp == head
// head 节点应该位于插入节点的前一个位置,否则无法插入
// 因为没有前驱指针,找不到前一个节点
Node temp = head;
boolean flag = false; // 添加的编号num如果存在了,就变成true
// 找到我们需要插入的位置节点temp,此节点是新插入node的前一个节点
while (true){
if (temp.next == null){
// 如果temp.next == null,则表示此temp是尾节点,且就是我们要找的节点
break;
}
if (temp.next.num > n.num){
// 如果temp后一个节点num > 插入节点num,则表示应该插入在
// temp节点和temp.next节点中间
// 此时的temp就是我们要找的节点
break;
}
else if (temp.next.num==n.num){
// 添加的编号已经存在了
flag = true;
break;
}
temp = temp.next; // 没有找到,判断下一个节点
}
if (flag){
System.out.println("插入的编号已经存在, 编号是: "+ n.num);
}
else{
// 插入到链表中,temo的后面
n.next = temp.next;
temp.next = n;
}
}
5.2.3 修改节点,删除节点
public void updateNodeByNum(Node n){
if (head.next==null){
System.out.println("链表为空!");
return;
}
// 找到需要修改的节点
Node temp = head.next;
boolean flag = false; // true表示找到了
while (true){
if (temp == null){
break; // 已经遍历结束了,没有找到,直接退出即可
}
if (temp.num == n.num){
// 找到了
flag = true;
break;
}
temp = temp.next;
}
if (flag){
// 找到了,修改节点的data数据
temp.data = n.data;
}
else {
// 没有找到
System.out.printf("没有找到节点编号为%d的节点,不能修改\n", n.num);
}
}
- 删除节点,由于是单链表,所以我们如果要删除红色箭头所指的节点,必须找到他的前一个节点(绿色所指),否则删除的节点前一个节点不能连上删除节点的后一个节点,形成不了新链表。
- 找到之后,temp.next = temp.next.next即可。
- 被删除的节点没有引用,就会被GC回收。

public void deleteNodeByNum(int num){
// 判断链表是否为空
if (head.next == null){
System.out.println("链表为空");
return;
}
Node temp = head;
boolean flag = false;
while (true){
if (temp.next.num == num){
// 找到了
flag = true;
break;
}
if (temp.next == null){
// 遍历结束, 没找到
break;
}
temp = temp.next; // 没找到,后移
}
if (flag){
temp.next = temp.next.next;
System.out.println("删除完毕");
}
else {
System.out.println("没有找到该节点,删除失败");
}
}
5.2.5 双向链表
5.2.6 循环链表
- 有单向循环链表,有双向循环链表
- 单向循环链表解决约瑟夫问题
- 我们用尾指针替代头指针,因为头指针找到头节点是O(1),找到尾节点是O(n)。所以让rear尾节点指向头节点的prior节点(即最后一个节点),此时找头是rear.next,找第一个节点(头节点后面的节点)为rear.next.next,找尾节点就是自己O1。
- 如何判断循环链表满了, temp.next != head 就是没有Full
- 判断空还是 temp.next == null
6 栈
6.1 应用场景

6.2 代码表示

package MyStack;
public class demo {
public static void main(String[] args) {
MyStack m1 = new MyStack(10);
m1.push(1);
m1.push(2);
m1.push(3);
m1.push(4);
m1.push(5);
m1.push(6);
m1.push(7);
m1.push(8);
m1.push(9);
m1.push(10);
m1.list();
System.out.println(m1.pop());
System.out.println(m1.pop());
System.out.println(m1.pop());
System.out.println(m1.pop());
System.out.println(m1.pop());
System.out.println(m1.pop());
System.out.println(m1.pop());
System.out.println(m1.pop());
System.out.println(m1.pop());
System.out.println(m1.pop());
}
}
class MyStack{
private int maxSize; // stack的大小,索引因该是-1,所以top<=maxSize-1
private int top = -1; // top栈顶
private int[] stack; // 数组模拟栈
public MyStack(int maxSize){
this.maxSize = maxSize;
stack = new int[this.maxSize];
}
// 判断栈是否满了
public boolean isFull(){
return top == maxSize-1;
}
// 判断栈空
public boolean isEmpty(){
return top == -1;
}
// 入栈
public void push(int value){
if (isFull()){
System.out.println("stack is full");
return;
}
stack[++top] = value;
}
// 出栈
public int pop(){
if (isEmpty()){
throw new RuntimeException("stack is empty");
}
return stack[top--];
}
// 遍历栈,从栈顶开始显示数据
public void list(){
if (isEmpty()){
System.out.println("stack is empty");
return;
}
// 下面方式不行,因为修改了top值,所以要用temp取代top
// while ( !(top<0) ){
// System.out.println(stack[top--]);
// }
int temp = top;
while ( !(temp<0) ){
System.out.println("stack[" + temp + "] = " + stack[temp]);
temp--;
}
// 视频方法
// for (int i=top; i>=0;i--){
// System.out.printf("stack[%d] = %d\n", i, stack[i]);
// }
}
}
6.3 四则运算案例

https://www.bilibili.com/video/BV1E4411H73v?p=33&spm_id_from=pageDriver



被折叠的 条评论
为什么被折叠?



