Java数据结构实现详解本文详细介绍了各种基本数据结构的Java实现,包括线性数据结构和非线性数据结构。每种数据结构都包含了基本操作方法、时间复杂度分析以及使用示例,可以作为学习数据结构与算法的参考资料。
一、线性数据结构线性数据结构是一种数据元素之间存在一对一关系的数据结构,元素按照线性顺序排列。
1. 数组 (Array)数组是最基本的数据结构,它在内存中是连续存储的,可以通过索引快速访问元素。
特点:
固定大小(静态数组)或可动态调整大小(动态数组)
随机访问元素的时间复杂度为 O(1)
在数组中间插入或删除元素的时间复杂度为 O(n)
主要操作及时间复杂度:
访问元素:O(1)
在末尾添加/删除元素:O(1) 均摊
在中间添加/删除元素:O(n)
查找元素:O(n)
数组结构示意图:
123+---+---+---+---+---+---+| 1 | 2 | 3 | 4 | 5 | 6 | -> 索引: 0,1,2,3,4,5+---+---+---+---+---+---+
代码示例:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384public class DynamicArray
操作示意图:
在索引2处插入元素:
1234567891011121314原数组:+---+---+---+---+---+---+| 1 | 2 | 3 | 4 | 5 | - |+---+---+---+---+---+---+移动元素:+---+---+---+---+---+---+| 1 | 2 | - | 3 | 4 | 5 |+---+---+---+---+---+---+插入新元素:+---+---+---+---+---+---+| 1 | 2 | 6 | 3 | 4 | 5 |+---+---+---+---+---+---+
应用场景:
需要频繁随机访问元素的场景
数据量固定的场景
需要存储基本数据类型的场景
多维数据结构(如矩阵)的实现
2. 链表 (LinkedList)链表是由一系列节点组成的线性集合,每个节点包含数据和指向下一个节点的引用。
3. 栈 (Stack)栈是一种后进先出(LIFO)的线性数据结构,只允许在一端(栈顶)进行插入和删除操作。
二、树形数据结构树形数据结构是一种非线性数据结构,它以层次方式存储数据,每个节点可以有多个子节点。
1. 二叉树 (Binary Tree)二叉树是每个节点最多有两个子节点的树形数据结构。
二叉树的结构示意图:
1234567 1 / \ 2 3 / \ \4 5 6 / 7
代码实现:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980public class BinaryTree
2. 二叉搜索树 (Binary Search Tree)二叉搜索树是一种特殊的二叉树,它满足以下性质:
左子树上所有节点的值均小于当前节点的值
右子树上所有节点的值均大于当前节点的值
左右子树也分别是二叉搜索树
二叉搜索树示意图:
12345 5 / \ 3 7 / \ \2 4 8
代码实现:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293public class BST
应用场景:
文件系统的目录结构
数据库索引
表达式解析
优先队列实现
3. 平衡二叉树 (AVL Tree)AVL树是一种自平衡二叉搜索树,任何节点的两个子树的高度差不超过1。
AVL树的结构示意图:
1234567 节点的平衡因子 = 左子树高度 - 右子树高度 4(0) / \ 2(-1) 6(0) / / \1(0) 5(0) 7(0)
代码实现:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107public class AVLTree
4. 红黑树 (Red-Black Tree)红黑树是一种自平衡二叉搜索树,通过节点的颜色来维持树的平衡。
红黑树的性质:
每个节点要么是红色,要么是黑色
根节点是黑色
每个叶子节点(NIL)是黑色
如果一个节点是红色的,则它的子节点必须是黑色的
从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点
红黑树示意图:
12345678 [B]7 / \ [B]3 [B]11 / \ \ [R]1 [R]5 [R]13[B] - 黑色节点[R] - 红色节点
应用场景:
Java的TreeMap和TreeSet的底层实现
Linux内核中的完全公平调度器
数据库索引
文件系统
三、哈希表 (Hash Table)哈希表是一种通过哈希函数将键映射到值的数据结构,它提供了快速的插入、删除和查找操作。
哈希表结构示意图:
123456789index → 链表(处理冲突) | [0] → [k1,v1] → [k5,v5] [1] → [k2,v2] [2] [3] → [k3,v3] [4] → [k4,v4] → [k6,v6] [5] [6]
代码实现:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899public class HashMap
四、图 (Graph)图是一种由顶点和边组成的非线性数据结构,用于表示元素之间的关系。
图的表示方法:
邻接矩阵:
12345 A B C DA 0 1 1 0B 1 0 1 1C 1 1 0 1D 0 1 1 0
邻接表:
1234A → [B, C]B → [A, C, D]C → [A, B, D]D → [B, C]
代码实现(邻接表):
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455public class Graph { private int V; // 顶点数 private LinkedList
应用场景:
社交网络
地图导航
网络拓扑
任务调度
特点:
后进先出(LIFO)
只能从栈顶访问元素
可以用数组或链表实现
栈的结构示意图:
12345678910 ↑ 栈顶+---+| 4 | <- 最后入栈的元素+---+| 3 |+---+| 2 |+---+| 1 | <- 最先入栈的元素+---+
基于数组实现的栈:
123456789101112131415161718192021222324252627282930public class ArrayStack
4. 队列 (Queue)队列是一种先进先出(FIFO)的线性数据结构,只允许在一端(队尾)进行插入操作,在另一端(队首)进行删除操作。
特点:
先进先出(FIFO)
队首删除,队尾添加
可以用数组或链表实现
队列的结构示意图:
1234队首 → +---+---+---+---+ | 1 | 2 | 3 | 4 | ← 队尾 +---+---+---+---+出队 ← 最先进入的元素 → 入队
循环队列实现:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051public class CircularQueue
应用场景:
栈:
函数调用栈
表达式求值
括号匹配
浏览器前进/后退功能
队列:
任务调度
消息队列
打印机打印任务
广度优先搜索
特点:
动态大小
插入和删除操作简单
不支持随机访问
链表结构示意图:
123456789单链表:+---+ +---+ +---+ +---+| 1 |-> | 2 |-> | 3 |-> | 4 |-> NULL+---+ +---+ +---+ +---+双向链表:+---+ +---+ +---+ +---+NULL <-| 1 |<-> | 2 |<-> | 3 |<-> | 4 |-> NULL+---+ +---+ +---+ +---+
主要操作及时间复杂度:
访问元素:O(n)
在头部添加/删除元素:O(1)
在尾部添加/删除元素:O(n)(单链表)或 O(1)(带尾指针的链表)
在中间添加/删除元素:O(n)
操作示意图:
在位置2插入新节点:
123456789原链表:+---+ +---+ +---+ +---+| 1 |-> | 2 |-> | 3 |-> | 4 |+---+ +---+ +---+ +---+插入新节点:+---+ +---+ +---+ +---+ +---+| 1 |-> | 2 |-> | 5 |-> | 3 |-> | 4 |+---+ +---+ +---+ +---+ +---+
代码示例:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102public class LinkedList
应用场景:
需要频繁插入和删除元素的场景
不需要随机访问的场景
内存空间要求灵活的场景
实现其他数据结构(如栈、队列、哈希表的拉链法等)prev = prev.next;}
Node retNode = prev.next;prev.next = retNode.next;retNode.next = null;size–;
return retNode.data;}
12345678910111213141516171819202122232425262728293031### 3. 栈 (Stack)栈是一种后进先出(LIFO)的线性表,只能在一端(栈顶)进行插入和删除操作。**特点:**- 后进先出(LIFO)- 只能从栈顶访问元素**主要操作及时间复杂度:**- 压栈(push):O(1)- 出栈(pop):O(1)- 查看栈顶元素(peek):O(1)**代码示例:**```java// 将元素压入栈顶public void push(E e) { array.addLast(e);}// 弹出栈顶元素public E pop() { return array.removeLast();}// 查看栈顶元素public E peek() { return array.getLast();}
应用场景:
函数调用栈
表达式求值
括号匹配
深度优先搜索
4. 队列 (Queue)队列是一种先进先出(FIFO)的线性表,只能在一端(队尾)进行插入操作,在另一端(队头)进行删除操作。
特点:
先进先出(FIFO)
只能从队头删除元素,从队尾添加元素
主要操作及时间复杂度:
入队(enqueue):O(1)
出队(dequeue):O(1)
查看队首元素(front):O(1)
应用场景:
任务调度
消息队列
广度优先搜索
二、非线性数据结构非线性数据结构是一种数据元素之间存在一对多关系的数据结构,元素不是按照线性顺序排列的。
1. 树 (Tree)树是一种层次结构,由节点组成,每个节点可以有多个子节点。
1.1 二叉树 (Binary Tree)二叉树是每个节点最多有两个子节点(左子节点和右子节点)的树结构。
特点:
每个节点最多有两个子节点
具有层次结构
主要操作及时间复杂度:
前序遍历:O(n)
中序遍历:O(n)
后序遍历:O(n)
层序遍历:O(n)
代码示例:
1234567891011121314151617181920212223242526272829303132// 前序遍历private void preOrder(Node node) { if (node == null) { return; } System.out.print(node.data + " "); preOrder(node.left); preOrder(node.right);}// 中序遍历private void inOrder(Node node) { if (node == null) { return; } inOrder(node.left); System.out.print(node.data + " "); inOrder(node.right);}// 后序遍历private void postOrder(Node node) { if (node == null) { return; } postOrder(node.left); postOrder(node.right); System.out.print(node.data + " ");}
1.2 二叉搜索树 (Binary Search Tree)二叉搜索树是一种特殊的二叉树,对于树中的每个节点,其左子树中的所有节点的值都小于该节点的值,右子树中的所有节点的值都大于该节点的值。
特点:
左子树上所有节点的值均小于根节点的值
右子树上所有节点的值均大于根节点的值
左右子树也分别为二叉搜索树
主要操作及时间复杂度:
查找:平均 O(log n),最坏 O(n)
插入:平均 O(log n),最坏 O(n)
删除:平均 O(log n),最坏 O(n)
1.3 平衡二叉树 (AVL Tree)平衡二叉树是一种特殊的二叉搜索树,它要求每个节点的左右子树的高度差不超过1。
特点:
是一棵二叉搜索树
每个节点的左右子树的高度差不超过1
主要操作及时间复杂度:
查找:O(log n)
插入:O(log n)
删除:O(log n)
2. 堆 (Heap)堆是一种特殊的完全二叉树,分为最大堆和最小堆。
特点:
最大堆:每个节点的值都大于或等于其子节点的值
最小堆:每个节点的值都小于或等于其子节点的值
是完全二叉树
主要操作及时间复杂度:
插入元素:O(log n)
删除最大/最小元素:O(log n)
获取最大/最小元素:O(1)
构建堆:O(n)
应用场景:
优先队列
堆排序
图算法(如Dijkstra算法)
3. 图 (Graph)图是由顶点和边组成的非线性数据结构,用于表示物体之间的关系。
特点:
由顶点和边组成
可以是有向的或无向的
可以是带权的或不带权的
表示方法:
邻接矩阵
邻接表
主要操作及时间复杂度:
添加顶点:O(1)
添加边:O(1)
深度优先搜索(DFS):O(V + E),其中V为顶点数,E为边数
广度优先搜索(BFS):O(V + E)
最短路径算法(如Dijkstra):O(V^2 + E)或O((V+E)logV)(使用优先队列)
最小生成树算法(如Prim):O(V^2)或O(ElogV)(使用优先队列)
代码示例:
123456789101112131415161718192021222324252627282930// 添加边public void addEdge(V from, V to, int weight) { // 确保顶点存在 addVertex(from); addVertex(to); int fromIndex = vertexMap.get(from); int toIndex = vertexMap.get(to); // 添加边 adjList.get(fromIndex).add(new Edge(toIndex, weight)); edgeCount++; // 如果是无向图,则添加反向边 if (!directed) { adjList.get(toIndex).add(new Edge(fromIndex, weight)); }}// 深度优先遍历private void dfsHelper(int vertex, boolean[] visited, List
4. 哈希表 (Hash Table)哈希表是一种能够实现关联数组的数据结构,它通过哈希函数将键映射到值。
特点:
通过键直接访问元素
平均查找、插入和删除时间复杂度为O(1)
可能存在哈希冲突
主要操作及时间复杂度:
插入:平均O(1),最坏O(n)
查找:平均O(1),最坏O(n)
删除:平均O(1),最坏O(n)
解决哈希冲突的方法:
链地址法(拉链法)
开放地址法(线性探测、二次探测、双重哈希)
应用场景:
实现字典或映射
缓存实现
集合实现
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145import java.util.*;public class CollectionExample { public static void main(String[] args) { // List 示例 System.out.println("List Example:"); // ArrayList 是基于动态数组实现的,适合频繁访问元素的场景。 List
import java.util.*;
public class CollectionExample {public static void main(String[] args) {// List 示例System.out.println(“List Example:”);// ArrayList 是基于动态数组实现的,适合频繁访问元素的场景。List list = new ArrayList<>();list.add(“Apple”); // 在末尾添加一个元素list.add(“Banana”);list.add(“Cherry”);
System.out.println("Initial List: " + list);
System.out.println("Size of List: " + list.size()); // 返回长度
System.out.println("Is List empty? " + list.isEmpty()); // 是否为空
String element = list.get(1); // 获取第i个元素
System.out.println("Element at index 1: " + element);
list.set(1, "Blueberry"); // 将第i个元素设置为val
System.out.println("Updated List: " + list);
list.clear(); // 清空
System.out.println("Cleared List: " + list);
// Stack 示例
System.out.println("\nStack Example:");
// Stack 是继承自 Vector 的后进先出 (LIFO) 栈。
Stack
stack.push(10); // 压入元素
stack.push(20);
stack.push(30);
System.out.println("Initial Stack: " + stack);
System.out.println("Size of Stack: " + stack.size()); // 返回长度
System.out.println("Is Stack empty? " + stack.empty()); // 栈是否为空
int topElement = stack.pop(); // 弹出栈顶元素,并返回栈顶元素
System.out.println("Popped Element: " + topElement);
System.out.println("Updated Stack after pop: " + stack);
int peekElement = stack.peek(); // 返回栈顶元素
System.out.println("Peeked Element: " + peekElement);
System.out.println("Stack after peek: " + stack);
stack.clear(); // 清空
System.out.println("Cleared Stack: " + stack);
// Queue 示例
System.out.println("\nQueue Example:");
// LinkedList 实现了 Queue 接口,适合用作队列。
Queue
queue.add("Red"); // 在队尾添加元素
queue.add("Green");
queue.add("Blue");
System.out.println("Initial Queue: " + queue);
System.out.println("Size of Queue: " + queue.size()); // 返回长度
System.out.println("Is Queue empty? " + queue.isEmpty()); // 是否为空
String headElement = queue.remove(); // 删除并返回队头
System.out.println("Removed Element: " + headElement);
System.out.println("Updated Queue after remove: " + queue);
String peekHead = queue.peek(); // 返回队头
System.out.println("Peeked Head Element: " + peekHead);
System.out.println("Queue after peek: " + queue);
queue.clear(); // 清空
System.out.println("Cleared Queue: " + queue);
// Set 示例
System.out.println("\nSet Example:");
// HashSet 不允许重复元素,内部使用哈希表实现。
Set
set.add("Carrot"); // 添加元素
set.add("Broccoli");
set.add("Carrot"); // Duplicate, will not be added
System.out.println("Initial Set: " + set);
System.out.println("Size of Set: " + set.size()); // 返回元素数
System.out.println("Does Set contain 'Carrot'? " + set.contains("Carrot")); // 是否包含某个元素
System.out.println("Does Set contain 'Potato'? " + set.contains("Potato"));
set.remove("Broccoli"); // 删除元素
System.out.println("Updated Set after remove: " + set);
set.clear(); // 清空
System.out.println("Cleared Set: " + set);
// TreeSet 示例
System.out.println("\nTreeSet Example:");
// TreeSet 不允许重复元素,内部使用红黑树实现,支持有序遍历。
TreeSet
treeSet.add(5); // 添加元素
treeSet.add(3);
treeSet.add(8);
System.out.println("Initial TreeSet: " + treeSet);
System.out.println("Ceiling of 4: " + treeSet.ceiling(4)); // 返回大于等于key的最小元素,不存在则返回null
System.out.println("Floor of 6: " + treeSet.floor(6)); // 返回小于等于key的最大元素,不存在则返回null
treeSet.clear(); // 清空
System.out.println("Cleared TreeSet: " + treeSet);
// Map 示例
System.out.println("\nMap Example:");
// HashMap 使用哈希表实现,键值对存储,不允许重复键。
Map
map.put("Alice", 25); // 添加关键字和其对应的值
map.put("Bob", 30);
map.put("Charlie", 35);
System.out.println("Initial Map: " + map);
System.out.println("Size of Map: " + map.size()); // 返回元素数
System.out.println("Value for key 'Bob': " + map.get("Bob")); // 返回关键字对应的值
System.out.println("Does Map contain key 'Alice'? " + map.containsKey("Alice")); // 是否包含关键字
System.out.println("Does Map contain key 'David'? " + map.containsKey("David"));
map.remove("Bob"); // 删除关键字
System.out.println("Updated Map after remove: " + map);
map.clear(); // 清空
System.out.println("Cleared Map: " + map);
// TreeMap 示例
System.out.println("\nTreeMap Example:");
// TreeMap 使用红黑树实现,键值对存储,按键排序,不允许重复键。
TreeMap
treeMap.put("Eve", 28); // 添加关键字和其对应的值
treeMap.put("Dan", 22);
treeMap.put("Frank", 27);
System.out.println("Initial TreeMap: " + treeMap);
System.out.println("Entry ceiling of 'Dean': " + treeMap.ceilingEntry("Dean").getValue()); // 返回大于等于key的最小元素,不存在则返回null
System.out.println("Entry floor of 'George': " + treeMap.floorEntry("George").getValue()); // 返回小于等于key的最大元素,不存在则返回null
treeMap.clear(); // 清空
System.out.println("Cleared TreeMap: " + treeMap);
}}
总结本文详细介绍了各种基本数据结构的Java实现,包括线性数据结构(数组、链表、栈、队列)和非线性数据结构(树、堆、图、哈希表)。每种数据结构都有其特定的应用场景和性能特点,在实际编程中,需要根据具体问题选择合适的数据结构。
通过学习和掌握这些数据结构的实现原理和使用方法,可以提高编程效率和代码质量,为解决复杂问题打下坚实的基础。