欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

从表到图:Java常用数据结构

程序员文章站 2022-05-24 18:46:23
Java数据结构一.稀疏数组当一个二维数组有许多相同的默认值,记录了许多没有意义的数据,可将该数组转换为稀疏数组实现思路:原数组总行数原数组总列数原数组有效值个数有效值1所在行有效值1所在列有效值1有效值2所在行有效值2所在列有效值2有效值n所在行有效值n所在列有效值npublic class main { public static void main(String[] args) { int a[][] = new...

Java数据结构

一.稀疏数组

当一个二维数组有许多相同的默认值,记录了许多没有意义的数据,可将该数组转换为稀疏数组

实现思路:

原数组总行数 原数组总列数 原数组有效值个数
有效值1所在行 有效值1所在列 有效值1
有效值2所在行 有效值2所在列 有效值2
有效值n所在行 有效值n所在列 有效值n
public class main { public static void main(String[] args) { int a[][] = new int[8][8]; //8*8数组 a[2][2] = 5; //3行3列,值5 a[4][6] = 9; //5行7列,值9 a[5][7] = 12; //6行8列,值12 int value = 0; //统计有效值个数 System.out.println("原始数组:"); for (int i = 0; i < a.length; i++) { for (int j = 0; j < a[0].length; j++) { if (a[i][j]!=0){ value++; } System.out.printf("%d\t",a[i][j]); } System.out.println(); } System.out.println("-------------------------------------"); //原始数组转换稀疏数组 int b[][] = new int[value+1][3];//创建稀疏数组,大小为有效值+1行和3列 b[0][0] = a.length; //原数组总行数 b[0][1] = a[0].length; //原数组总列数 b[0][2] = value; //原数组有效值个数 int row = 1; //稀疏数组有效数据行行数,记录有效值 for (int i = 1; i < a.length; i++) { for (int j = 0; j < a[0].length; j++) { if (a[i][j]!=0){ b[row][0] = i; //第一列:有效值所在行 b[row][1] = j; //第二列:有效值所在列 b[row][2] = a[i][j]; //第三列:有效值 row++; //记录完一行跳转下一行 } } } System.out.println("稀疏数组:"); for (int[] hang : b) { for (int i : hang) System.out.printf("%d\t",i); System.out.println(); } System.out.println("-------------------------------------"); //稀疏数组还原 int c[][] = new int[b[0][0]][b[0][1]]; //创建数组 for (int i = 1; i < b.length; i++) { for (int j = 0; j < b[1].length; j++) { //在b[i][0]行,[b[i][1]列添加有效值:b[i][2] c[b[i][0]][b[i][1]] = b[i][2]; } } System.out.println("还原数组:"); for (int[] hang : c) { for (int i : hang) System.out.printf("%d\t",i); System.out.println(); } } } 

从表到图:Java常用数据结构

二.链表

链表是一种物理存储单元上非连续、非顺序的存储结构,元素的逻辑顺序是通过链表中的指针链接次序实现的

链表由一个个节点组成,每个节点都指向下一个节点,最后一个节点的下个节点指向null

public class main { public static void main(String[] args) { Student student1 = new Student(1, "小A"); Student student2 = new Student(2, "小B"); Student student3 = new Student(3, "小C"); Student student4 = new Student(4, "小D"); Student student5 = new Student(5, "小E"); SingleLinkedList singleLinkedList = new SingleLinkedList(); singleLinkedList.add(student1); singleLinkedList.add(student2); singleLinkedList.add(student3); singleLinkedList.add(student4); singleLinkedList.add(student5); System.out.println("学生链表"); singleLinkedList.list(); //修改节点 Student newStudent = new Student(3, "小明"); singleLinkedList.update(newStudent); System.out.println("修改后:"); singleLinkedList.list(); //删除节点 singleLinkedList.del(4); singleLinkedList.del(7); System.out.println("删除后:"); singleLinkedList.list(); } } class SingleLinkedList { //初始化头节点 private Student head = new Student(0, ""); public void add(Student student) { //移动遍历 Student temp = head; //遍历链表,找到最后 while (true) { //找到链表的最后 if (temp.next == null) { break; } //如果没有找到最后, 将temp后移 temp = temp.next; } //将最后这个节点的next指向新的节点 temp.next = student; } public void update(Student newStudent) { Student temp = head; while (true) { if(temp.next==null){ System.out.println("找不到该元素"); break; } //找到要修改的元素 if (temp.num == newStudent.num) { temp.name = newStudent.name; break; } //如果没有找到最后, 将temp后移 temp = temp.next; } } public void del(int num) { Student temp = head; while (true) { if(temp.next==null){ System.out.println("找不到该元素"); break; } if (temp.next.num == num) { //将待删除的节点替换为它的下一个节点 temp.next = temp.next.next; break; } temp = temp.next; //temp后移,遍历 } } public void list() { Student temp = head; while (true) { //判断是否到链表最后 if (temp.next == null) { break; } System.out.println(temp.next); //将temp后移 temp = temp.next; } } } class Student { public int num; public String name; public Student next; //指向下一个节点 public Student(int num, String name) { this.num = num; this.name = name; } @Override public String toString() { return "Student [编号=" + num + ", name=" + name +"]"; } } 

从表到图:Java常用数据结构

三.队列(Queue)

队列是一个有序列表,可用数组和链表实现,遵循先入先出原则

队列在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。rear端称为队尾,front端称为队头

public class main { public static void main(String[] args) { Queue<String> queue = new LinkedList<String>(); Scanner scanner = new Scanner(System.in); for (int i = 0; i < 5; i++) { queue.offer(scanner.next()); } System.out.println(queue); for (int i = 0; i < 2; i++) { System.out.println("poll元素:"+queue.poll()); } System.out.println(queue); System.out.println("element元素:"+queue.element()); System.out.println(queue); for (int i = 0; i < 2; i++) { System.out.println("再次添加:"+queue.offer(scanner.next())); } System.out.println(queue); } }

从表到图:Java常用数据结构

四.栈(Stack)

栈是一个有序列表,可用数组和链表实现,遵循先入后出原则

栈中的元素只允许在同一端进行插入和删除,能进行增删的一端为栈顶(top),固定的一端为栈底(bottom)

public class main { public static void main(String[] args) { Stack<String> stack = new Stack<String>(); Scanner scanner = new Scanner(System.in); for (int i = 0; i < 5; i++) { stack.push(scanner.next()); } System.out.println(stack); for (int i = 0; i < 2; i++) { System.out.println("弹出元素:"+stack.pop()); } System.out.println(stack); for (int i = 0; i < 2; i++) { System.out.println("加入元素:"+stack.push(scanner.next())); } System.out.println(stack); } }

从表到图:Java常用数据结构

五.哈希表(Hash table)

根据关键码值(Key value)而直接进行访问的数据结构。通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表

哈希表是由一块地址连续的数组空间构成的,其中每个数组都是一个链表,数组的作用在于快速寻址查找,链表的作用在于快速插入和删除元素,因此,哈希表可以被认为就是链表的数组

public class main { public static void main(String[] args) { //创建哈希表 HashTab hashTab = new HashTab(7); String key = ""; Scanner scanner = new Scanner(System.in); while(true) { System.out.println("add:  添加员工"); System.out.println("list: 显示员工"); System.out.println("find: 查找员工"); System.out.println("del: 删除员工"); key = scanner.next(); switch (key) { case "add": System.out.println("输入添加id和名字"); int id = scanner.nextInt(); String name = scanner.next(); Emp emp = new Emp(id, name); hashTab.add(emp); break; case "list": hashTab.list(); break; case "find": System.out.println("输入查找id和名字"); int id1 = scanner.nextInt(); String name1 = scanner.next(); Emp emp1 = new Emp(id1, name1); hashTab.findEmpById(emp1); break; case "del": System.out.println("输入删除id和名字"); int id2 = scanner.nextInt(); String name2 = scanner.next(); Emp emp2 = new Emp(id2, name2); hashTab.del(emp2); break; } } } } //创建HashTab class HashTab { //创建员工链表数组,管理多条链表 private EmpLinkedList[] empLinkedList; private int size; //表示有多少条链表 //构造器 public HashTab(int size) { this.size = size; //初始化empLinkedListArray empLinkedList = new EmpLinkedList[size]; //初始化每个链表 for(int i = 0; i < size; i++) { empLinkedList[i] = new EmpLinkedList(); } } //添加雇员 public void add(Emp emp) { //使用散列函数确定添加哪条链表 int List_id = hashFun(emp.id); empLinkedList[List_id].add(emp); } //遍历所有的链表 public void list() { for(int i = 0; i < size; i++) { empLinkedList[i].list(i); } } //根据输入的信息,查找员工 public void findEmpById(Emp emp) { //使用散列函数确定到哪条链表查找 int List_id = hashFun(emp.id); Emp emp_f = empLinkedList[List_id].findEmpById(emp); if(emp_f != null) { System.out.printf("第%d条链表,id=%d,name=%s\n", List_id+1,emp_f.id,emp_f.name); }else{ System.out.println("在哈希表中,没有找到该员工"); } } //根据输入信息删除员工 public void del(Emp emp){ int List_id = hashFun(emp.id); empLinkedList[List_id].del(emp); } //散列函数 public int hashFun(int id) { //return id % size;   除法散列法 //这里表示id为几就插入哪条链表 return id-1; } } //创建EmpLinkedList链表 class EmpLinkedList { //头节点 Emp head = new Emp(0,"",null); //添加员工 public void add(Emp emp) { //定义移动节点,从头节点开始 Emp curEmp = head; while(true) { //循环后移节点,找到链表末端跳出 if(curEmp.next == null) { break; } curEmp = curEmp.next; } //此时curEmp为链表最后一个员工,将新员工赋值给curEmp的下个节点 curEmp.next = emp; } //遍历链表的员工信息 public void list(int no) { if(head.next==null) { //说明链表为空 System.out.println("第 "+(no+1)+" 链表为空"); return; } System.out.print("第 "+(no+1)+" 链表的信息为"); //定义移动节点,头节点为空,从第二个节点开始 Emp curEmp = head.next; while(true) { System.out.printf(" => id=%d name=%s\t", curEmp.id, curEmp.name); if(curEmp.next == null) {//遍历到末端 break; } curEmp = curEmp.next; //后移 } System.out.println(); } //根据id查找 public Emp findEmpById(Emp emp) { //定义移动节点,从头节点开始 Emp curEmp = head; while(true) { if(curEmp.name.equals(emp.name)) {//找到指定员工 break; } if(curEmp.next == null) {//遍历当前链表没有找到该员工 curEmp = null; break; } curEmp = curEmp.next;//后移 } return curEmp; } public void del(Emp emp){ //定义移动节点,从头节点开始 Emp curEmp = head; while(true) { if (curEmp.next == null){ System.out.println("找不到该员工"); break; } //循环后移节点,找到待删除节点 if(curEmp.next.name.equals(emp.name)) { //将待删除的节点替换为它的下一个节点 curEmp.next = curEmp.next.next; break; } curEmp = curEmp.next; } } } //员工类 class Emp { public int list_id; public int id; public String name; public Emp next; public Emp(int id, String name,Emp next) { this.id = id; this.name = name; this.next = next; } public Emp(int id, String name) { this.id = id; this.name = name; } }

从表到图:Java常用数据结构

六.二叉树(Binary tree)

有序数组的优势在于二分查找,链表的优势在于数据的插入和删除。但是在有序数组中插入数据很慢,在链表中查找数据效率很低。综合以上情况,二叉树可以利用链表和有序数组的优势,同时可以合并有序数组和链表的优势

二叉树是n个有限元素的集合,由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树

存储方式有顺序存储、链式存储

public class main { public static void main(String[] args) { //创建二叉树,添加节点 BinaryTree binaryTree = new BinaryTree(); StudentNode root = new StudentNode(1, "AA"); StudentNode node2 = new StudentNode(2, "BB"); StudentNode node3 = new StudentNode(3, "CC"); StudentNode node4 = new StudentNode(4, "DD"); StudentNode node5 = new StudentNode(5, "EE"); StudentNode node6 = new StudentNode(6, "FF"); root.setLeft(node2); root.setRight(node3); node2.setLeft(node4); node3.setLeft(node5); node3.setRight(node6); //添加根节点 binaryTree.setRoot(root); System.out.println("前序遍历"); // 1,2,4,3,5,6 binaryTree.preOrder(); System.out.println("中序遍历"); binaryTree.infixOrder(); // 4,2,1,5,3,6 System.out.println("后序遍历"); binaryTree.postOrder(); // 4,2,5,6,3,1 //前序遍历,查找5次 System.out.println("前序遍历查找"); StudentNode resNode1 = binaryTree.preOrderSearch(5); if (resNode1 != null) { System.out.printf("num=%d name=%s", resNode1.getNum(), resNode1.getName()); } else { System.out.printf("没有找到%d号学生", 5); } //中序遍历,查找4次 System.out.println("中序遍历查找"); StudentNode resNode2 = binaryTree.infixOrderSearch(5); if (resNode2 != null) { System.out.printf("num=%d name=%s", resNode2.getNum(), resNode2.getName()); } else { System.out.printf("没有找到%d号学生", 5); } //后序遍历,查找3次 System.out.println("后序遍历查找"); StudentNode resNode3 = binaryTree.postOrderSearch(5); if (resNode3 != null) { System.out.printf("num=%d name=%s", resNode3.getNum(), resNode3.getName()); } else { System.out.printf("没有找到%d号学生", 5); } //删除结点 System.out.println("删除前,前序遍历"); binaryTree.preOrder(); //  1,2,4,3,5,6 binaryTree.delNode(5); System.out.println("删除后,前序遍历"); binaryTree.preOrder(); //1,2,4,3,6 } } //定义BinaryTree二叉树 class BinaryTree { private StudentNode root; public void setRoot(StudentNode root) { this.root = root; } //删除结点 public void delNode(int num) { root.delNode(num); } //前序遍历 public void preOrder() { this.root.preOrder(); } //中序遍历 public void infixOrder() { this.root.infixOrder(); } //后序遍历 public void postOrder() { this.root.postOrder(); } //前序遍历查找 public StudentNode preOrderSearch(int num) { return root.preOrderSearch(num); } //中序遍历查找 public StudentNode infixOrderSearch(int num) { return root.infixOrderSearch(num); } //后序遍历查找 public StudentNode postOrderSearch(int num) { return this.root.postOrderSearch(num); } } //先创建StudentNode 结点 class StudentNode { private int num; private String name; private StudentNode left; //默认null private StudentNode right; //默认null public StudentNode(int num, String name) { this.num = num; this.name = name; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } public String getName() { return name; } public void setName(String name) { this.name = name; } public StudentNode getLeft() { return left; } public void setLeft(StudentNode left) { this.left = left; } public StudentNode getRight() { return right; } public void setRight(StudentNode right) { this.right = right; } @Override public String toString() { return "StudentNode{" + "num=" + num + ", name='" + name + '}'; } //递归删除结点 //如果删除的节点是叶子节点,则删除该节点 //如果删除的节点是非叶子节点,则删除该子树 public void delNode(int num) { //如果当前结点的左子结点不为空,并且左子结点 就是要删除结点,就将this.left = null; 并且就返回(结束递归删除) if(this.left != null && this.left.num == num) { this.left = null; return; } //如果当前结点的右子结点不为空,并且右子结点 就是要删除结点,就将this.right= null ;并且就返回(结束递归删除) if(this.right != null && this.right.num == num) { this.right = null; return; } //左子树递归删除 if(this.left != null) { this.left.delNode(num); } //右子树递归删除 if(this.right != null) { this.right.delNode(num); } } //前序遍历实现 public void preOrder() { //输出父结点 System.out.println(this); //递归向左子树前序遍历 if(this.left != null) { this.left.preOrder(); } //递归向右子树前序遍历 if(this.right != null) { this.right.preOrder(); } } //中序遍历实现 public void infixOrder() { //递归向左子树中序遍历 if(this.left != null) { this.left.infixOrder(); } //输出父结点 System.out.println(this); //递归向右子树中序遍历 if(this.right != null) { this.right.infixOrder(); } } //后序遍历实现 public void postOrder() { //递归向右子树后序遍历 if(this.left != null) { this.left.postOrder(); } //递归向右子树后序遍历 if(this.right != null) { this.right.postOrder(); } //输出父结点 System.out.println(this); } //前序遍历查找实现 //前序遍历顺序:1,2,4,3,5,6 //1开始,按照遍历顺序依次对比 public StudentNode preOrderSearch(int num) { System.out.println("查找中......"); //this表示当前节点(父节点),对比目标节点,是则返回this if(this.num == num) { return this; } StudentNode resNode = null; //遍历左子树 if(this.left != null) { resNode = this.left.preOrderSearch(num); } if(resNode != null) { return resNode; } //遍历右子树 if(this.right != null) { resNode = this.right.preOrderSearch(num); } return resNode; } //中序遍历查找 //中序遍历顺序:4,2,1,5,3,6 //1开始,找到4,按照遍历顺序依次对比 public StudentNode infixOrderSearch(int num) { StudentNode resNode = null; //遍历左子树 if(this.left != null) { resNode = this.left.infixOrderSearch(num); } if(resNode != null) { return resNode; } System.out.println("查找中......"); //this表示当前节点(父节点),对比目标节点,是则返回this if(this.num == num) { return this; } //遍历右子树 if(this.right != null) { resNode = this.right.infixOrderSearch(num); } return resNode; } //后序遍历查找 //后序遍历顺序:4,2,5,6,3,1 //1开始,找到4,按照遍历顺序依次对比 public StudentNode postOrderSearch(int num) { StudentNode resNode = null; //遍历左子树 if(this.left != null) { resNode = this.left.postOrderSearch(num); } if(resNode != null) { return resNode; } //遍历右子树 if(this.right != null) { resNode = this.right.postOrderSearch(num); } if(resNode != null) { return resNode; } System.out.println("查找中......"); //this表示当前节点(父节点),对比目标节点,是则返回this if(this.num == num) { return this; } return resNode; } }

从表到图:Java常用数据结构

七.图(Graph)

图是由顶点的有穷非空集合和顶点之间边的集合组成, 通常表示为: G(V,E), 其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合

图形结构中,数据元素(顶点)之间具有任意关系,图中任意两个数据元素之间都可能相关

图可以用邻接矩阵或邻接表表示

public class main { public static void main(String[] args) { //顶点的个数 int n = 8; //顶点值数组 String Vertexs[] = {"A", "B", "C", "D", "E", "F", "G", "H"}; //创建图对象 Graph graph = new Graph(n); //循环添加顶点 for(String vertex: Vertexs) { graph.insertVertex(vertex); } //定义各个边 graph.insertEdge(0, 1, 1); graph.insertEdge(0, 2, 1); graph.insertEdge(1, 3, 1); graph.insertEdge(1, 4, 1); graph.insertEdge(3, 7, 1); graph.insertEdge(4, 7, 1); graph.insertEdge(2, 5, 1); graph.insertEdge(2, 6, 1); graph.insertEdge(5, 6, 1); //显示图基本信息 graph.showGraph(); System.out.println("深度优先:"); graph.dfs(); // 1->2->4->8->5->3->6->7 System.out.println(); System.out.println("广度优先:"); graph.bfs(); // 1->2->3->4->5->6->7->8 System.out.println(); //显示邻结矩阵 graph.showEdges(); } } //定义图的属性 class Graph{ private ArrayList<String> vertexList; //存储顶点集合 private int[][] edges; //存储图对应的邻结矩阵 private int numOfEdges; //表示边的数目 private boolean[] isVisited; //记录某个结点是否被访问 //构造器 public Graph(int n) { //初始化邻结矩阵和顶点集合 edges = new int[n][n]; vertexList = new ArrayList<String>(n); numOfEdges = 0; } //插入结点 public void insertVertex(String vertex) { vertexList.add(vertex); } //添加边 public void insertEdge(int v1, int v2, int weight) { //设置邻接矩阵值,可以直接两个相连的顶点weight为1 edges[v1][v2] = weight; edges[v2][v1] = weight; //统计边数 numOfEdges++; } //显示图的基本信息 public void showGraph(){ System.out.println("顶点总数:"+vertexList.size()); System.out.println("边总数:"+numOfEdges); } //显示图对应的邻接矩阵 public void showEdges() { System.out.println("邻接矩阵:"); for(int[] hang : edges) { for (int lie : hang) System.out.printf("%d\t",lie); System.out.println(); } } //返回结点i(下标)对应的数据 public String getValueByIndex(int i) { return vertexList.get(i); } //深度优先遍历算法 private void dfs(boolean[] isVisited, int i) { //首先我们访问该顶点,输出 System.out.print(getValueByIndex(i) + "->"); //将顶点设置为已经访问 isVisited[i] = true; //查找顶点i的第一个邻接顶点w int w = getFirstNeighbor(i); while(w != -1) {//找到第一个邻接顶点w //如果w顶点未被访问,继续递归查找它的第一个邻接顶点 if(!isVisited[w]) { dfs(isVisited, w); } //如果该顶点已经被访问过,将i,w传过去,查找i的第二个邻接顶点 w = getNextNeighbor(i, w); } } //对dfs进行一个重载, 遍历我们所有的顶点,并进行dfs public void dfs() { isVisited = new boolean[vertexList.size()]; //遍历所有的顶点,进行dfs[回溯] for(int i = 0; i < vertexList.size(); i++) { if(!isVisited[i]) { dfs(isVisited, i); } } } //广度优先遍历算法 private void bfs(boolean[] isVisited, int i) { int u ; // 表示队列的头顶点对应下标 int w ; // 邻接顶点w //队列,记录顶点访问的顺序 LinkedList queue = new LinkedList(); //访问顶点,输出顶点信息 System.out.print(getValueByIndex(i) + "->"); //标记为已访问 isVisited[i] = true; //将顶点加入队列 queue.addLast(i); while( !queue.isEmpty()) { //取出队列的头顶点下标 u = (Integer)queue.removeFirst(); //得到第一个邻接顶点的下标 w w = getFirstNeighbor(u); while(w != -1) {//找到第一个邻接顶点w //如果w顶点未访问 if(!isVisited[w]) { //输出 System.out.print(getValueByIndex(w) + "->"); //标记已经访问 isVisited[w] = true; //入队 queue.addLast(w); } //以u为前驱点,找w后面的下一个邻接顶点 w = getNextNeighbor(u, w); } } } //遍历所有的结点,都进行广度优先搜索 public void bfs() { isVisited = new boolean[vertexList.size()]; for(int i = 0; i < vertexList.size(); i++) { if(!isVisited[i]) { bfs(isVisited, i); } } } //获取顶点的第一个邻接顶点 public int getFirstNeighbor(int v1) { for(int j = 0; j < vertexList.size(); j++) { if(edges[v1][j] > 0) { return j; } } return -1; } //根据前一个邻接顶点的下标来获取下一个邻接顶点 public int getNextNeighbor(int v1, int v2) { for(int j = v2 + 1; j < vertexList.size(); j++) { if(edges[v1][j] > 0) { return j; } } return -1; } } 

从表到图:Java常用数据结构

本文地址:https://blog.csdn.net/gugfjj/article/details/108041686

相关标签: 数据结构 Java