线性结构和非线性结构、稀疏数组、队列、链表(LinkedList)

线性结构和非线性结构、稀疏数组、队列、链表(LinkedList)

一、线性结构和非线性结构

线性结构:

1)线性绪构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系

2)线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构。顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的3)链式存储的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息4)线性结构常见的有:数组、队列、链表和栈,后面我们会详细讲解.

非线性结构:

非线性结构包括:二维数组,多维数组,广义表,树结构,图结构

二、稀疏数组

基本介绍:当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组。

稀疏数组的处理方法是:1)记录数组一共有几行几列,有多少个不同的值2)把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模

//实现原始数组和稀疏数组的相互转换;并存入文件进行保存和读取

package com.zjl.sparsearray;

import java.io.*;

public class SparseArray {

public static void main(String[] args) throws IOException {

String file = "C:\\Users\\wenman\\Desktop\\test\\sapraseArr.txt";

//定义一个原始的数组arr[11][11],并赋值arr[1][2] = 1, arr[2][3] = 2

int[][] arr = new int[11][11];

arr[1][2] = 1;

arr[2][3] = 2;

arr[3][4] = 2;

//查看原始数据

for (int[] arr_:arr) {

for (int num:arr_) {

System.out.printf("%d ",num);

}

System.out.println();

}

//获取原始数据中的数据个数

int nums = 0;

for (int[] arr_:arr) {

for (int num:arr_) {

if (num != 0){

nums++;

}

}

}

//将原始数组转变为稀疏数组

int[][] sparseArr = new int[nums + 1][3];

sparseArr[0][0] = 11;

sparseArr[0][1] = 11;

sparseArr[0][2] = nums;

int count = 0;

for (int i = 0; i < 11; i++) {

for (int j = 0; j < 11; j++) {

if (arr[i][j] != 0){

count++;

sparseArr[count][0] = i;

sparseArr[count][1] = j;

sparseArr[count][2] = arr[i][j];

}

}

}

/**

* 将稀疏数组存入到文件中

*/

FileWriter fileWriter = new FileWriter(file);

for (int i = 0;i

fileWriter.write(sparseArr[i][0]+" "+sparseArr[i][1]+" "+sparseArr[i][2]+"\n");

}

fileWriter.close();

//输出稀疏数组结构

for (int[] arr_:sparseArr) {

for (int num:arr_

) {

System.out.printf("%d ",num);

}

System.out.println();

}

/**

* 从文件中读取数组

*/

BufferedReader bufferedReader = new BufferedReader(new FileReader(file));

String line = null;

int lineNum = 0;

//初始化原始数组

String[] s = bufferedReader.readLine().split(" ");

int[][] arrs = new int[Integer.parseInt(s[0])][Integer.parseInt(s[1])];

//获取原始数组中的值并赋值

while ((line = bufferedReader.readLine())!=null) {

String[] s1 = line.split(" ");

arrs[Integer.parseInt(s1[0])][Integer.parseInt(s1[1])] = Integer.parseInt(s1[2]);

}

bufferedReader.close();

//将稀疏数组变成原始数组

// int[][] arrs = new int[sparseArr[0][0]][sparseArr[0][1]];

// for (int i = 1 ; i < sparseArr.length; i ++) {

// arrs[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];

// }

for (int[] arr_:arrs) {

for (int num:arr_) {

System.out.printf("%d ",num);

}

System.out.println();

}

}

}

三、队列

介绍:

1、队列是一个有序列表,可以用数组或者链表来实现,

2、遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出

用数组模拟:

在入队和出队时:

因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front及rear分别记录队列前后端的下标,front会随着数据输出而改变(变大),而rear则是随着数据输入而改变(变大)尾进头出,先进先出

三、单链表链表(Linked List)

介绍:链表是有序的列表

1)链表是以节点的方式来存储

2)每个节点包括data域(数据域)和next域(指向下一个节点)

3)链表的各个节点不一定是连续的

4)链表分带头节点的链表和没有头结点的链表,根据实际的需求来确定

单链表存储结构示意图:(内存)

单链表(带头结点)逻辑结构示意图

单链表的常见面试题:

//node结点

public class HeroNode {

public int no;

public String name;

public HeroNode next;

public HeroNode(int no, String name) {

this.no = no;

this.name = name;

}

@Override

public String toString() {

return

"no=" + no +

", name=" + name ;

}

}

//单链表相关函数

import jdk.nashorn.internal.objects.annotations.Where;

import java.util.Stack;

public class HeroLinkedList {

private HeroNode head = new HeroNode(0,"");

public HeroNode getHead() {

return head;

}

//添加或者修改存在的节点

public void addOrUPdateNode(HeroNode node){

if (node == null) {

System.out.println("加入失败,不能加入空对象");

}

HeroNode temp = head;

while (true){

if (temp.next == null) {

temp.next = node;

break;

}

if (temp.next.no == node.no) {

temp.next.name = node.name;

break;

}

if ( temp.next.no > node.no && temp.no < node.no ) {

node.next = temp.next;

temp.next = node;

break;

}

temp = temp.next;

}

}

//删除某个节点

public void deleteNode(int i){

HeroNode temp = head;

while (temp.next != null) {

if (temp.next.no == i) {

temp.next = temp.next.next;

return;

}

temp = temp.next;

}

System.out.println("删除失败,没有这个节点!");

}

//展示所有的节点

public void allNode(){

HeroNode temp = head;

while (temp.next != null) {

System.out.println(temp.next);

temp = temp.next;

}

}

//返回节点的所有个数

public int nodeSum(HeroNode head){

if (head == null || head.next == null) {

return 0;

}

int num = 0;

HeroNode temp = head;

while (temp.next != null) {

temp = temp.next;

num++;

}

return num;

}

//查找单链表的倒数第N个节点

public void findHeroNode(HeroNode head,int n){

if (n <= 0 || n > nodeSum(head)) {

System.out.println("单链表中没有这个节点!");

return;

}

int m = nodeSum(head)-n;

HeroNode temp = head;

while (m>0){

temp = temp.next;

m--;

}

System.out.println(temp.next);

}

//反转单向链表并输出

public void reverseHeroNode(){

//定义一个新的头结点

HeroNode reNode = new HeroNode(0,"");

boolean exange = false;

HeroNode tempHead = null;

if (head.next != null) {

tempHead = head.next;

}

HeroNode temp;

while (tempHead != null) {

temp = tempHead;

tempHead = tempHead.next;

temp.next = reNode.next;

reNode.next = temp;

exange = true;

}

if (exange){

head = reNode;

}

}

//逆序打印单向链表

public void reversePrint(){

if (head == null) {

return;

}

//使用栈,先进后出的顺序,按正常顺序存入栈中,出栈时就是逆序

Stack hero = new Stack<>();

HeroNode temp = head;

while (temp.next != null) {

hero.add(temp.next);

temp = temp.next;

}

while (!hero.isEmpty()) {

System.out.println(hero.pop());

}

}

//合并两条单链表,使得排序之后仍然有序

public void mergesLinkedList(HeroLinkedList list){

if (list == null){

return;

}

if (list.head.next == null){

return;

}

HeroNode temp = list.head.next;

HeroNode tempAdd = null;

while (temp != null){

tempAdd = temp;

temp = temp.next;

this.addOrUPdateNode(tempAdd);

}

}

}

//测试单链表相关方法

package com.zjl.linked_list;

public class testLinkedList {

public static void main(String[] args) {

HeroNode node1 = new HeroNode(1, "张三");

HeroNode node5 = new HeroNode(5, "二麻子");

HeroNode node6 = new HeroNode(6, "王五");

HeroNode node2 = new HeroNode(2, "李四");

HeroNode node3 = new HeroNode(3, "大头");

HeroNode node4 = new HeroNode(4, "小鬼");

// HeroNode node31 = new HeroNode(3, "王五$$");

HeroLinkedList list = new HeroLinkedList();

HeroLinkedList list1 = new HeroLinkedList();

//添加节点

list.addOrUPdateNode(node1);

list.addOrUPdateNode(node2);

list.addOrUPdateNode(node5);

list.addOrUPdateNode(node6);

list1.addOrUPdateNode(node3);

list1.addOrUPdateNode(node4);

//展示所有节点

list.allNode();

list1.allNode();

//删除某个节点

// list.deleteNode(3);

// list.allNode();

//要删除的节点不存在时

// list.deleteNode(3);

// list.deleteNode(5);

//获取节点的总个数

System.out.println(list.nodeSum(list.getHead()));

//找到单向链表的倒数第n个数

// list.findHeroNode(list.getHead(),1);

//反转单链表

// list.reverseHeroNode();

// list.allNode();

//逆序输出单向链表

// list.reversePrint();

//合并两个单链表,并且合并之后依然有顺序

list.mergesLinkedList(list1);

list.allNode();

}

}

二、双向链表

特点:

1、具有next指向下一个指针,也有per指向上一个指针

2、查找方式,可以向前也可以向后

3、可以自我删除

单向链表和双向链表的不同:

//双向链表结点的定义

public class HeroNode {

public int no;

public String name;

public HeroNode next;

public HeroNode per;

public HeroNode(int no, String name) {

this.no = no;

this.name = name;

}

@Override

public String toString() {

return

"no=" + no +

", name=" + name ;

}

}

//双向链表相关的方法

import com.zjl.linked_list.HeroNode;

import jdk.nashorn.internal.objects.annotations.Where;

public class HeroDoubleLinkedList {

HeroNode head = new HeroNode(0, "");

//双向链表的添加和更改

public void addDoubleLinkedList(HeroNode node){

if (node == null){

return;

}

HeroNode temp = head;

while (true) {

if (temp.next == null){

temp.next = node;

node.per = temp;

break;

}

if (temp.next.no == node.no) {

temp.next.name=node.name;

break;

}

if ( temp.no < node.no && temp.next.no > node.no) {

node.next = temp.next;

temp.next.per = node;

temp.next = node;

node.per = temp;

break;

}

temp = temp.next;

}

}

//展示所有的双向链表

public void allDoubleLinkedList(){

if (head == null) {

return;

}

HeroNode temp = head;

while (temp.next != null) {

System.out.println(temp.next);

temp = temp.next;

}

}

//删除双向链表的节点

public void deleteDoubleLinkedliseNode(int node){

if (head == null && node > 0) {

return;

}

boolean loop = true;

HeroNode temp = head;

while (temp != null) {

if (temp.no == node) {

temp.next.per = temp.per;

temp.per.next = temp.next;

loop = false;

break;

}

if (loop) {

temp = temp.next;

}

}

System.out.println("双向链表中,没有这个结点!");

}

}

//测试双向链表;

import com.sun.org.apache.bcel.internal.generic.NEW;

import com.zjl.linked_list.HeroNode;

public class testDoubleLinkedList {

public static void main(String[] args) {

HeroDoubleLinkedList list = new HeroDoubleLinkedList();

HeroNode node1 = new HeroNode(1, "张三");

HeroNode node2 = new HeroNode(2, "李四");

HeroNode node3 = new HeroNode(3, "二麻子");

HeroNode node4 = new HeroNode(4, "王五");

//添加结点到双向链表中

list.addDoubleLinkedList(node1);

list.addDoubleLinkedList(node2);

list.addDoubleLinkedList(node3);

list.addDoubleLinkedList(node4);

//展示双向链表中的所有节点

list.allDoubleLinkedList();

//删除双向链表中的一个结点

list.deleteDoubleLinkedliseNode(2);

list.allDoubleLinkedList();

}

}

三、单向环形链表

约瑟夫问题:

约瑟夫问题的解决思路,用一个头指针表示当前到达的位置,用一个辅助指针表示当前位置的前一个位置(方便进行删除操作),当first移动到要出圈的位置,就进行出圈操作,当已经到达first和辅助指针指向同一个位置时,就表示环形链表中自剩下一个结点,结束出圈

完成一个关于约瑟夫问题的求解:

//定义将用到的结点

package com.zjl.josepfu;

public class Child {

public int no;

public Child next;

public Child(int no) {

this.no = no;

}

public int getNo() {

return no;

}

@Override

public String toString() {

return "Child=" + no;

}

}

//定义在环形链表中,解决约瑟夫问题的方法

package com.zjl.josepfu;

public class Josepfu {

public Child first = new Child(-1);

//添加环形链表

public void addNewChild(int nums){

if (nums < 1) {

System.out.println("输入的数量不合适!");

}

Child temp = first;

Child boy = first;

if (nums == 1) {

boy.next = new Child(1);

boy = boy.next;

boy.next = temp.next;

}

else{

for (int i = 1; i <= nums; i++) {

boy.next = new Child(i);

boy = boy.next;

boy.next = temp.next;

}

}

}

//输出环形链表中的所有结点

public void allJosepfu(){

if (first.next == null) {

System.out.println("该环没有结点");

return;

}

Child temp = first.next;

Child boy = first.next;

while (true) {

System.out.println(boy);

boy = boy.next;

if (boy == temp) {

break;

}

}

}

//删除环中的某一个节点

public void deleteJosepfyNode(Child node){

if (node == null) {

System.out.println("删除的节点不能为空!");

}

if (first.next == null) {

System.out.println("该环形链表为空");

}

Child temp = first.next;

Child boy = first.next;

while (true) {

if (temp.no == node.no) {

first.next = temp.next;

}

if (boy.no == boy.next.no && boy.next.no == node.no) {

first.next = null;

break;

}

if (boy.next.no == node.no ) {

boy.next = boy.next.next;

break;

}

boy = boy.next;

}

}

//获取环形链表的节点个数

public int allJosepfuNum(){

if (first.next == null) {

System.out.println("该环没有结点");

return 0;

}

int num = 0;

Child temp = first.next;

Child boy = first.next;

while (true) {

// System.out.println(boy);

boy = boy.next;

num++;

if (boy == temp) {

break;

}

}

return num;

}

//进行约瑟夫环的操作,根据输入的确定的起点和间隔,在已知的环形链表进行约瑟夫问题求解

public void testJosepfu(int k,int m){

if (k > allJosepfuNum() || k <= 0) {

System.out.println("该环中没有该起点!");

return;

}

Child boy = first;//找到头结点,开始一个环链表

//找到环形链表中的对应的起点k值

while (boy.next.no != k) {

boy = boy.next;

}

//循环的次数

int m_temp = m;

while (true) {

boy = boy.next;

m_temp--;

//每向下找一个结点就将间隔距离减一

//当距离变为0时找到对应的值输出,并从环链表中删除该值,并重新设置距离

if (m_temp == 0) {

System.out.println(boy);

deleteJosepfyNode(boy);

m_temp = m;

}

//当环形链表中只剩下一个结点的时候,就输出该结点并退出循环

if (allJosepfuNum() == 1) {

allJosepfu();

break;

}

}

}

//直接对约瑟夫问题进行求解,可以直接确定环的长度,起点,和间隔的距离

public void testJosepfu(int k, int m, int num) {

if (k < 1 || k > num) {

System.out.println("输入的参数有问题!");

}

addNewChild(num);

Child helper = first.next;

while (helper.next != first.next) {

helper = helper.next;

}

first = first.next;

while (first.no != k){

first = first.next;

helper = helper.next;

}

int temp = m-1;

while (true){

first = first.next;

helper = helper.next;

temp--;

if (helper == first){

allJosepfu();

break;

}

if (temp == 0) {

System.out.println(first);

first = first.next;

helper.next = first;

temp = m-1;

}

}

}

}

//测试方法

package com.zjl.josepfu;

public class TestJosepfu {

public static void main(String[] args) {

Josepfu josepfu = new Josepfu();

Josepfu josepfu1 = new Josepfu();

//给约瑟夫环加入结点

josepfu.addNewChild(5);

// System.out.println(josepfu.allJosepfuNum());

// //输出所有的结点

// josepfu.allJosepfu();

// josepfu.deleteJosepfyNode(new Child(3));

// josepfu.allJosepfu();

// System.out.println(josepfu.allJosepfuNum());

//测试进行约瑟夫实验

josepfu.testJosepfu(1,2);

// josepfu.allJosepfu();

//输入起点,间隔,以及环形链表的大小,进行约瑟夫问题的解决

josepfu1.testJosepfu(1,2,5);

}

}

相关推荐

win11电脑屏幕色彩如何调整?色彩设置在哪里修改?
球球大作战卡白房怎么弄 最新卡白房教程分享
线上365bet正网

球球大作战卡白房怎么弄 最新卡白房教程分享

07-04 👁️‍🗨️ 9399
凌渡是什么级别车
365彩票app下载苹果版

凌渡是什么级别车

10-28 👁️‍🗨️ 1645
拼多多怎么签到?拼多多的每日签到在哪里
365彩票app下载苹果版

拼多多怎么签到?拼多多的每日签到在哪里

06-27 👁️‍🗨️ 2108
暗影神偷
365彩票app下载苹果版

暗影神偷

01-10 👁️‍🗨️ 9145
动物森友会蝼蛄如何抓 蝼蛄抓捕方法
线上365bet正网

动物森友会蝼蛄如何抓 蝼蛄抓捕方法

11-14 👁️‍🗨️ 6711
2024年十大网红:一念天堂,一念地狱
bt365注册

2024年十大网红:一念天堂,一念地狱

02-06 👁️‍🗨️ 9845
10种“长寿食物”吃对了,效果>每天运动?!(附独家清单)
高質抵玩,評測 Fujifilm XF 16
bt365注册

高質抵玩,評測 Fujifilm XF 16

01-11 👁️‍🗨️ 3139