Java数据结构实现详解 本文详细介绍了各种基本数据结构的Java实现,包括线性数据结构和非线性数据结构。每种数据结构都包含了基本操作方法、时间复杂度分析以及使用示例,可以作为学习数据结构与算法的参考资料。
一、线性数据结构 线性数据结构是一种数据元素之间存在一对一关系的数据结构,元素按照线性顺序排列。
1. 数组 (Array) 数组是最基本的数据结构,它在内存中是连续存储的,可以通过索引快速访问元素。
特点:
固定大小(静态数组)或可动态调整大小(动态数组)
随机访问元素的时间复杂度为 O(1)
在数组中间插入或删除元素的时间复杂度为 O(n)
主要操作及时间复杂度:
访问元素:O(1)
在末尾添加/删除元素:O(1) 均摊
在中间添加/删除元素:O(n)
查找元素:O(n)
数组结构示意图:
1 2 3 +---+---+---+---+---+---+ | 1 | 2 | 3 | 4 | 5 | 6 | -> 索引: 0,1,2,3,4,5 +---+---+---+---+---+---+
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 public class DynamicArray <E> { private E[] data; private int size; @SuppressWarnings("unchecked") public DynamicArray (int capacity) { data = (E[])new Object [capacity]; size = 0 ; } public int getSize () { return size; } public int getCapacity () { return data.length; } public void add (int index, E e) { if (index < 0 || index > size) throw new IllegalArgumentException ("Add failed. Require index >= 0 and index <= size." ); if (size == data.length) resize(2 * data.length); for (int i = size - 1 ; i >= index ; i --) data[i + 1 ] = data[i]; data[index] = e; size ++; } public E get (int index) { if (index < 0 || index >= size) throw new IllegalArgumentException ("Get failed. Index is illegal." ); return data[index]; } public void set (int index, E e) { if (index < 0 || index >= size) throw new IllegalArgumentException ("Set failed. Index is illegal." ); data[index] = e; } public boolean contains (E e) { for (int i = 0 ; i < size; i++) { if (data[i].equals(e)) return true ; } return false ; } public E remove (int index) { if (index < 0 || index >= size) throw new IllegalArgumentException ("Remove failed. Index is illegal." ); E ret = data[index]; for (int i = index + 1 ; i < size; i++) data[i - 1 ] = data[i]; size--; data[size] = null ; if (size == data.length / 4 && data.length / 2 != 0 ) resize(data.length / 2 ); return ret; } @SuppressWarnings("unchecked") private void resize (int newCapacity) { E[] newData = (E[])new Object [newCapacity]; for (int i = 0 ; i < size; i++) newData[i] = data[i]; data = newData; } }
操作示意图:
在索引2处插入元素:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 原数组: +---+---+---+---+---+---+ | 1 | 2 | 3 | 4 | 5 | - | +---+---+---+---+---+---+ 移动元素: +---+---+---+---+---+---+ | 1 | 2 | - | 3 | 4 | 5 | +---+---+---+---+---+---+ 插入新元素: +---+---+---+---+---+---+ | 1 | 2 | 6 | 3 | 4 | 5 | +---+---+---+---+---+---+
应用场景:
需要频繁随机访问元素的场景
数据量固定的场景
需要存储基本数据类型的场景
多维数据结构(如矩阵)的实现
2. 链表 (LinkedList) 链表是由一系列节点组成的线性集合,每个节点包含数据和指向下一个节点的引用。
3. 栈 (Stack) 栈是一种后进先出(LIFO)的线性数据结构,只允许在一端(栈顶)进行插入和删除操作。
二、树形数据结构 树形数据结构是一种非线性数据结构,它以层次方式存储数据,每个节点可以有多个子节点。
1. 二叉树 (Binary Tree) 二叉树是每个节点最多有两个子节点的树形数据结构。
二叉树的结构示意图:
1 2 3 4 5 6 7 1 / \ 2 3 / \ \ 4 5 6 / 7
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 public class BinaryTree <E> { private class Node { public E data; public Node left, right; public Node (E data) { this .data = data; left = null ; right = null ; } } private Node root; private int size; public BinaryTree () { root = null ; size = 0 ; } public void preOrder () { preOrder(root); } private void preOrder (Node node) { if (node == null ) return ; System.out.println(node.data); preOrder(node.left); preOrder(node.right); } public void inOrder () { inOrder(root); } private void inOrder (Node node) { if (node == null ) return ; inOrder(node.left); System.out.println(node.data); inOrder(node.right); } public void postOrder () { postOrder(root); } private void postOrder (Node node) { if (node == null ) return ; postOrder(node.left); postOrder(node.right); System.out.println(node.data); } public void levelOrder () { if (root == null ) return ; Queue<Node> queue = new LinkedList <>(); queue.add(root); while (!queue.isEmpty()) { Node cur = queue.remove(); System.out.println(cur.data); if (cur.left != null ) queue.add(cur.left); if (cur.right != null ) queue.add(cur.right); } } }
2. 二叉搜索树 (Binary Search Tree) 二叉搜索树是一种特殊的二叉树,它满足以下性质:
左子树上所有节点的值均小于当前节点的值
右子树上所有节点的值均大于当前节点的值
左右子树也分别是二叉搜索树
二叉搜索树示意图:
1 2 3 4 5 5 / \ 3 7 / \ \ 2 4 8
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 public class BST <E extends Comparable <E>> { private class Node { public E data; public Node left, right; public Node (E data) { this .data = data; left = null ; right = null ; } } private Node root; private int size; public BST () { root = null ; size = 0 ; } public void add (E e) { root = add(root, e); } private Node add (Node node, E e) { if (node == null ) { size++; return new Node (e); } if (e.compareTo(node.data) < 0 ) node.left = add(node.left, e); else if (e.compareTo(node.data) > 0 ) node.right = add(node.right, e); return node; } public boolean contains (E e) { return contains(root, e); } private boolean contains (Node node, E e) { if (node == null ) return false ; if (e.compareTo(node.data) == 0 ) return true ; else if (e.compareTo(node.data) < 0 ) return contains(node.left, e); else return contains(node.right, e); } public E removeMin () { E ret = minimum(); root = removeMin(root); return ret; } private Node removeMin (Node node) { if (node.left == null ) { Node rightNode = node.right; node.right = null ; size--; return rightNode; } node.left = removeMin(node.left); return node; } public E minimum () { if (size == 0 ) throw new IllegalArgumentException ("BST is empty" ); return minimum(root).data; } private Node minimum (Node node) { if (node.left == null ) return node; return minimum(node.left); } }
应用场景:
文件系统的目录结构
数据库索引
表达式解析
优先队列实现
3. 平衡二叉树 (AVL Tree) AVL树是一种自平衡二叉搜索树,任何节点的两个子树的高度差不超过1。
AVL树的结构示意图:
1 2 3 4 5 6 7 节点的平衡因子 = 左子树高度 - 右子树高度 4(0) / \ 2(-1) 6(0) / / \ 1(0) 5(0) 7(0)
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 public class AVLTree <E extends Comparable <E>> { private class Node { public E data; public Node left, right; public int height; public Node (E data) { this .data = data; left = null ; right = null ; height = 1 ; } } private Node root; private int size; private int getHeight (Node node) { if (node == null ) return 0 ; return node.height; } private int getBalanceFactor (Node node) { if (node == null ) return 0 ; return getHeight(node.left) - getHeight(node.right); } private Node rightRotate (Node y) { Node x = y.left; Node T3 = x.right; x.right = y; y.left = T3; y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1 ; x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1 ; return x; } private Node leftRotate (Node y) { Node x = y.right; Node T2 = x.left; x.left = y; y.right = T2; y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1 ; x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1 ; return x; } public void add (E e) { root = add(root, e); } private Node add (Node node, E e) { if (node == null ) { size++; return new Node (e); } if (e.compareTo(node.data) < 0 ) node.left = add(node.left, e); else if (e.compareTo(node.data) > 0 ) node.right = add(node.right, e); else return node; node.height = 1 + Math.max(getHeight(node.left), getHeight(node.right)); int balanceFactor = getBalanceFactor(node); if (balanceFactor > 1 && getBalanceFactor(node.left) >= 0 ) return rightRotate(node); if (balanceFactor < -1 && getBalanceFactor(node.right) <= 0 ) return leftRotate(node); if (balanceFactor > 1 && getBalanceFactor(node.left) < 0 ) { node.left = leftRotate(node.left); return rightRotate(node); } if (balanceFactor < -1 && getBalanceFactor(node.right) > 0 ) { node.right = rightRotate(node.right); return leftRotate(node); } return node; } }
4. 红黑树 (Red-Black Tree) 红黑树是一种自平衡二叉搜索树,通过节点的颜色来维持树的平衡。
红黑树的性质:
每个节点要么是红色,要么是黑色
根节点是黑色
每个叶子节点(NIL)是黑色
如果一个节点是红色的,则它的子节点必须是黑色的
从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点
红黑树示意图:
1 2 3 4 5 6 7 8 [B]7 / \ [B]3 [B]11 / \ \ [R]1 [R]5 [R]13 [B] - 黑色节点 [R] - 红色节点
应用场景:
Java的TreeMap和TreeSet的底层实现
Linux内核中的完全公平调度器
数据库索引
文件系统
三、哈希表 (Hash Table) 哈希表是一种通过哈希函数将键映射到值的数据结构,它提供了快速的插入、删除和查找操作。
哈希表结构示意图:
1 2 3 4 5 6 7 8 9 index → 链表(处理冲突) | [0] → [k1,v1] → [k5,v5] [1] → [k2,v2] [2] [3] → [k3,v3] [4] → [k4,v4] → [k6,v6] [5] [6]
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 public class HashMap <K, V> { private static final int DEFAULT_CAPACITY = 16 ; private static final float LOAD_FACTOR = 0.75f ; private class Node { public K key; public V value; public Node next; public Node (K key, V value, Node next) { this .key = key; this .value = value; this .next = next; } } private Node[] table; private int size; public HashMap () { table = new Node [DEFAULT_CAPACITY]; size = 0 ; } private int hash (K key) { return Math.abs(key.hashCode() % table.length); } public void put (K key, V value) { int index = hash(key); Node node = table[index]; while (node != null ) { if (node.key.equals(key)) { node.value = value; return ; } node = node.next; } table[index] = new Node (key, value, table[index]); size++; if (size >= table.length * LOAD_FACTOR) resize(); } public V get (K key) { int index = hash(key); Node node = table[index]; while (node != null ) { if (node.key.equals(key)) return node.value; node = node.next; } return null ; } public V remove (K key) { int index = hash(key); Node node = table[index]; Node prev = null ; while (node != null ) { if (node.key.equals(key)) { if (prev == null ) table[index] = node.next; else prev.next = node.next; size--; return node.value; } prev = node; node = node.next; } return null ; } private void resize () { Node[] oldTable = table; table = new Node [oldTable.length * 2 ]; size = 0 ; for (Node node : oldTable) { while (node != null ) { put(node.key, node.value); node = node.next; } } } }
四、图 (Graph) 图是一种由顶点和边组成的非线性数据结构,用于表示元素之间的关系。
图的表示方法:
邻接矩阵:
1 2 3 4 5 A B C D A 0 1 1 0 B 1 0 1 1 C 1 1 0 1 D 0 1 1 0
邻接表:
1 2 3 4 A → [B, C] B → [A, C, D] C → [A, B, D] D → [B, C]
代码实现(邻接表):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public class Graph { private int V; private LinkedList<Integer>[] adj; @SuppressWarnings("unchecked") public Graph (int v) { V = v; adj = new LinkedList [v]; for (int i = 0 ; i < v; i++) adj[i] = new LinkedList <>(); } public void addEdge (int v, int w) { adj[v].add(w); adj[w].add(v); } public void BFS (int s) { boolean [] visited = new boolean [V]; Queue<Integer> queue = new LinkedList <>(); visited[s] = true ; queue.add(s); while (!queue.isEmpty()) { s = queue.poll(); System.out.print(s + " " ); for (int n : adj[s]) { if (!visited[n]) { visited[n] = true ; queue.add(n); } } } } public void DFS (int s) { boolean [] visited = new boolean [V]; DFSUtil(s, visited); } private void DFSUtil (int v, boolean [] visited) { visited[v] = true ; System.out.print(v + " " ); for (int n : adj[v]) { if (!visited[n]) DFSUtil(n, visited); } } }
应用场景:
特点:
后进先出(LIFO)
只能从栈顶访问元素
可以用数组或链表实现
栈的结构示意图:
1 2 3 4 5 6 7 8 9 10 ↑ 栈顶 +---+ | 4 | <- 最后入栈的元素 +---+ | 3 | +---+ | 2 | +---+ | 1 | <- 最先入栈的元素 +---+
基于数组实现的栈:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class ArrayStack <E> { private Array<E> array; public ArrayStack (int capacity) { array = new Array <>(capacity); } public int getSize () { return array.getSize(); } public boolean isEmpty () { return array.isEmpty(); } public void push (E e) { array.addLast(e); } public E pop () { return array.removeLast(); } public E peek () { return array.getLast(); } }
4. 队列 (Queue) 队列是一种先进先出(FIFO)的线性数据结构,只允许在一端(队尾)进行插入操作,在另一端(队首)进行删除操作。
特点:
先进先出(FIFO)
队首删除,队尾添加
可以用数组或链表实现
队列的结构示意图:
1 2 3 4 队首 → +---+---+---+---+ | 1 | 2 | 3 | 4 | ← 队尾 +---+---+---+---+ 出队 ← 最先进入的元素 → 入队
循环队列实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class CircularQueue <E> { private E[] data; private int front, tail; private int size; public CircularQueue (int capacity) { data = (E[]) new Object [capacity + 1 ]; front = 0 ; tail = 0 ; size = 0 ; } public boolean isEmpty () { return front == tail; } public boolean isFull () { return (tail + 1 ) % data.length == front; } public void enqueue (E e) { if (isFull()) throw new IllegalStateException ("Queue is full" ); data[tail] = e; tail = (tail + 1 ) % data.length; size++; } public E dequeue () { if (isEmpty()) throw new IllegalStateException ("Queue is empty" ); E ret = data[front]; data[front] = null ; front = (front + 1 ) % data.length; size--; return ret; } public E getFront () { if (isEmpty()) throw new IllegalStateException ("Queue is empty" ); return data[front]; } public int getSize () { return size; } }
应用场景:
栈:
函数调用栈
表达式求值
括号匹配
浏览器前进/后退功能
队列:
特点:
链表结构示意图:
1 2 3 4 5 6 7 8 9 单链表: +---+ +---+ +---+ +---+ | 1 |-> | 2 |-> | 3 |-> | 4 |-> NULL +---+ +---+ +---+ +---+ 双向链表: +---+ +---+ +---+ +---+ NULL <-| 1 |<-> | 2 |<-> | 3 |<-> | 4 |-> NULL +---+ +---+ +---+ +---+
主要操作及时间复杂度:
访问元素:O(n)
在头部添加/删除元素:O(1)
在尾部添加/删除元素:O(n)(单链表)或 O(1)(带尾指针的链表)
在中间添加/删除元素:O(n)
操作示意图:
在位置2插入新节点:
1 2 3 4 5 6 7 8 9 原链表: +---+ +---+ +---+ +---+ | 1 |-> | 2 |-> | 3 |-> | 4 | +---+ +---+ +---+ +---+ 插入新节点: +---+ +---+ +---+ +---+ +---+ | 1 |-> | 2 |-> | 5 |-> | 3 |-> | 4 | +---+ +---+ +---+ +---+ +---+
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 public class LinkedList <E> { private class Node { public E data; public Node next; public Node (E data, Node next) { this .data = data; this .next = next; } } private Node dummyHead; private int size; public LinkedList () { dummyHead = new Node (null , null ); size = 0 ; } public int getSize () { return size; } public boolean isEmpty () { return size == 0 ; } public void addFirst (E e) { add(0 , e); } public void addLast (E e) { add(size, e); } public void add (int index, E e) { if (index < 0 || index > size) throw new IllegalArgumentException ("Add failed. Illegal index." ); Node prev = dummyHead; for (int i = 0 ; i < index; i++) prev = prev.next; prev.next = new Node (e, prev.next); size++; } public E get (int index) { if (index < 0 || index >= size) throw new IllegalArgumentException ("Get failed. Illegal index." ); Node cur = dummyHead.next; for (int i = 0 ; i < index; i++) cur = cur.next; return cur.data; } public void set (int index, E e) { if (index < 0 || index >= size) throw new IllegalArgumentException ("Set failed. Illegal index." ); Node cur = dummyHead.next; for (int i = 0 ; i < index; i++) cur = cur.next; cur.data = e; } public boolean contains (E e) { Node cur = dummyHead.next; while (cur != null ) { if (cur.data.equals(e)) return true ; cur = cur.next; } return false ; } public E remove (int index) { if (index < 0 || index >= size) throw new IllegalArgumentException ("Remove failed. Index is illegal." ); Node prev = dummyHead; for (int i = 0 ; i < index; i++) prev = prev.next; Node retNode = prev.next; prev.next = retNode.next; retNode.next = null ; size--; return retNode.data; } }
应用场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ### 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)
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 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)(使用优先队列)
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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<V> result) { visited[vertex] = true ; result.add(vertexList.get(vertex)); for (Edge edge : adjList.get(vertex)) { if (!visited[edge.to]) { dfsHelper(edge.to, visited, result); } } }
4. 哈希表 (Hash Table) 哈希表是一种能够实现关联数组的数据结构,它通过哈希函数将键映射到值。
特点:
通过键直接访问元素
平均查找、插入和删除时间复杂度为O(1)
可能存在哈希冲突
主要操作及时间复杂度:
插入:平均O(1),最坏O(n)
查找:平均O(1),最坏O(n)
删除:平均O(1),最坏O(n)
解决哈希冲突的方法:
链地址法(拉链法)
开放地址法(线性探测、二次探测、双重哈希)
应用场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 import java.util.*;public class CollectionExample { public static void main (String[] args) { System.out.println("List Example:" ); List<String> 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 ); System.out.println("Element at index 1: " + element); list.set(1 , "Blueberry" ); System.out.println("Updated List: " + list); list.clear(); System.out.println("Cleared List: " + list); System.out.println("\nStack Example:" ); Stack<Integer> stack = new 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); System.out.println("\nQueue Example:" ); Queue<String> queue = new LinkedList <>(); 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); System.out.println("\nSet Example:" ); Set<String> set = new HashSet <>(); set.add("Carrot" ); set.add("Broccoli" ); set.add("Carrot" ); 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); System.out.println("\nTreeSet Example:" ); TreeSet<Integer> treeSet = new 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 )); System.out.println("Floor of 6: " + treeSet.floor(6 )); treeSet.clear(); System.out.println("Cleared TreeSet: " + treeSet); System.out.println("\nMap Example:" ); Map<String, Integer> map = new HashMap <>(); 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); System.out.println("\nTreeMap Example:" ); TreeMap<String, Integer> treeMap = new 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()); System.out.println("Entry floor of 'George': " + treeMap.floorEntry("George" ).getValue()); treeMap.clear(); System.out.println("Cleared TreeMap: " + treeMap); } }
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<Integer> stack = new 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<String> queue = new LinkedList<>();
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<String> set = new HashSet<>();
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<Integer> treeSet = new 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<String, Integer> map = new HashMap<>();
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<String, Integer> treeMap = new 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实现,包括线性数据结构(数组、链表、栈、队列)和非线性数据结构(树、堆、图、哈希表)。每种数据结构都有其特定的应用场景和性能特点,在实际编程中,需要根据具体问题选择合适的数据结构。
通过学习和掌握这些数据结构的实现原理和使用方法,可以提高编程效率和代码质量,为解决复杂问题打下坚实的基础。