java数据结构与算法面试题汇总
快速排序是一种高效的排序算法,它采用分治的思想,通过选择一个基准元素,将数组分为两部分,小于基准元素的部分和大于基准元素的部分,然后分别对这两部分进行递归排序。插入排序是一种简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。
目录
(四)二叉搜索树、平衡二叉树、红黑树、哈夫曼树、前缀树的理解
一、排序算法
(一)快速排序
快速排序是一种高效的排序算法,它采用分治的思想,通过选择一个基准元素,将数组分为两部分,小于基准元素的部分和大于基准元素的部分,然后分别对这两部分进行递归排序。
Java 实现:
public class QuickSort {
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pivotIndex = partition(arr, low, high);
quickSort(arr, low, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, high);
}
}
private static int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] < pivot) {
i++;
swap(arr, i, j);
}
}
swap(arr, i + 1, high);
return i + 1;
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
(二)归并排序
归并排序也是一种分治算法,它将数组分成两个子数组,分别对两个子数组进行排序,然后将两个有序的子数组合并成一个有序的数组。
Java 实现:
public class MergeSort {
public static void mergeSort(int[] arr) {
if (arr.length <= 1) {
return;
}
int mid = arr.length / 2;
int[] left = new int[mid];
int[] right = new int[arr.length - mid];
System.arraycopy(arr, 0, left, 0, mid);
System.arraycopy(arr, mid, right, 0, arr.length - mid);
mergeSort(left);
mergeSort(right);
merge(arr, left, right);
}
private static void merge(int[] arr, int[] left, int[] right) {
int i = 0, j = 0, k = 0;
while (i < left.length && j < right.length) {
if (left[i] < right[j]) {
arr[k++] = left[i++];
} else {
arr[k++] = right[j++];
}
}
while (i < left.length) {
arr[k++] = left[i++];
}
while (j < right.length) {
arr[k++] = right[j++];
}
}
}
(三)冒泡排序
冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。
Java 实现:
public class BubbleSort {
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
}
}
(四)插入排序
插入排序是一种简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
Java 实现:
public class InsertionSort {
public static void insertionSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
}
二、数据结构实现
(一)LRU 缓存
LRU(Least Recently Used)缓存是一种最近最少使用的缓存淘汰策略。当缓存满时,会淘汰最近最少使用的元素。
Java 实现:
import java.util.LinkedHashMap;
import java.util.Map;
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75f, true);
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > capacity;
}
}
(二)哈希表
哈希表是一种根据关键码值(Key value)而直接进行访问的数据结构。
Java 实现:
class HashTable<K, V> {
private static final int INITIAL_CAPACITY = 16;
private Entry<K, V>[] table;
class Entry<K, V> {
K key;
V value;
Entry<K, V> next;
Entry(K key, V value) {
this.key = key;
this.value = value;
}
}
public HashTable() {
table = new Entry[INITIAL_CAPACITY];
}
public void put(K key, V value) {
int hash = hash(key);
Entry<K, V> entry = table[hash];
if (entry == null) {
table[hash] = new Entry<>(key, value);
} else {
while (entry.next!= null &&!entry.key.equals(key)) {
entry = entry.next;
}
if (entry.key.equals(key)) {
entry.value = value;
} else {
entry.next = new Entry<>(key, value);
}
}
}
public V get(K key) {
int hash = hash(key);
Entry<K, V> entry = table[hash];
while (entry!= null) {
if (entry.key.equals(key)) {
return entry.value;
}
entry = entry.next;
}
return null;
}
private int hash(K key) {
return key.hashCode() % table.length;
}
}
(三)基于数组的栈
栈是一种后进先出(LIFO)的数据结构。
Java 实现:
class ArrayStack<T> {
private T[] stack;
private int top;
private int capacity;
@SuppressWarnings("unchecked")
public ArrayStack(int capacity) {
this.capacity = capacity;
stack = (T[]) new Object[capacity];
top = -1;
}
public void push(T item) {
if (top == capacity - 1) {
throw new StackOverflowError();
}
stack[++top] = item;
}
public T pop() {
if (top == -1) {
throw new EmptyStackException();
}
return stack[top--];
}
public boolean isEmpty() {
return top == -1;
}
}
(四)基于优先队列的最小堆
最小堆是一种完全二叉树,每个节点的值都小于或等于其子节点的值。
Java 实现:
import java.util.ArrayList;
import java.util.List;
class MinHeap<T extends Comparable<T>> {
private List<T> heap;
public MinHeap() {
heap = new ArrayList<>();
}
public void insert(T item) {
heap.add(item);
int index = heap.size() - 1;
while (index > 0 && heap.get(parent(index)).compareTo(item) > 0) {
swap(index, parent(index));
index = parent(index);
}
}
public T extractMin() {
if (heap.isEmpty()) {
return null;
}
T min = heap.get(0);
heap.set(0, heap.get(heap.size() - 1));
heap.remove(heap.size() - 1);
heapify(0);
return min;
}
private void heapify(int index) {
int left = leftChild(index);
int right = rightChild(index);
int smallest = index;
if (left < heap.size() && heap.get(left).compareTo(heap.get(smallest)) < 0) {
smallest = left;
}
if (right < heap.size() && heap.get(right).compareTo(heap.get(smallest)) < 0) {
smallest = right;
}
if (smallest!= index) {
swap(index, smallest);
heapify(smallest);
}
}
private int parent(int index) {
return (index - 1) / 2;
}
private int leftChild(int index) {
return 2 * index + 1;
}
private int rightChild(int index) {
return 2 * index + 2;
}
private void swap(int i, int j) {
T temp = heap.get(i);
heap.set(i, heap.get(j));
heap.set(j, temp);
}
}
三、链表操作
(一)反转单链表
反转一个单链表,即将链表的节点顺序反转。
Java 实现:
class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
}
}
public class ReverseLinkedList {
public static ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr!= null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
}
(二)找出两个单链表的交点
给定两个单链表,判断它们是否有交点,如果有,返回交点的节点。
Java 实现:
public class IntersectionOfLinkedLists {
public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode pA = headA;
ListNode pB = headB;
while (pA!= pB) {
pA = pA == null? headB : pA.next;
pB = pB == null? headA : pB.next;
}
return pA;
}
}
(三)检测单链表中是否有环
判断一个单链表中是否存在环。
Java 实现:
public class LinkedListCycle {
public static boolean hasCycle(ListNode head) {
if (head == null) {
return false;
}
ListNode slow = head;
ListNode fast = head;
while (fast!= null && fast.next!= null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
return true;
}
}
return false;
}
}
四、树的操作
(一)判断二叉树是否平衡
判断一棵二叉树是否是平衡二叉树,即该二叉树的任意节点的左右子树高度差不超过 1。
Java 实现:
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
}
}
public class BalancedBinaryTree {
public static boolean isBalanced(TreeNode root) {
return height(root)!= -1;
}
private static int height(TreeNode node) {
if (node == null) {
return 0;
}
int leftHeight = height(node.left);
if (leftHeight == -1) {
return -1;
}
int rightHeight = height(node.right);
if (rightHeight == -1) {
return -1;
}
if (Math.abs(leftHeight - rightHeight) > 1) {
return -1;
}
return Math.max(leftHeight, rightHeight) + 1;
}
}
(二)二叉树的遍历
- 前序遍历:先访问根节点,然后遍历左子树,最后遍历右子树。
- 中序遍历:先遍历左子树,然后访问根节点,最后遍历右子树。
- 后序遍历:先遍历左子树,然后遍历右子树,最后访问根节点。
Java 实现(递归方式):
public class BinaryTreeTraversal {
public static void preOrderTraversal(TreeNode root) {
if (root == null) {
return;
}
System.out.print(root.val + " ");
preOrderTraversal(root.left);
preOrderTraversal(root.right);
}
public static void inOrderTraversal(TreeNode root) {
if (root == null) {
return;
}
inOrderTraversal(root.left);
System.out.print(root.val + " ");
inOrderTraversal(root.right);
}
public static void postOrderTraversal(TreeNode root) {
if (root == null) {
return;
}
postOrderTraversal(root.left);
postOrderTraversal(root.right);
System.out.print(root.val + " ");
}
}
Java 实现(非递归方式):
import java.util.Stack;
public class BinaryTreeTraversal {
public static void preOrderTraversalNonRecursive(TreeNode root) {
if (root == null) {
return;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
System.out.print(node.val + " ");
if (node.right!= null) {
stack.push(node.right);
}
if (node.left!= null) {
stack.push(node.left);
}
}
}
public static void inOrderTraversalNonRecursive(TreeNode root) {
if (root == null) {
return;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode curr = root;
while (curr!= null ||!stack.isEmpty()) {
while (curr!= null) {
stack.push(curr);
curr = curr.left;
}
curr = stack.pop();
System.out.print(curr.val + " ");
curr = curr.right;
}
}
public static void postOrderTraversalNonRecursive(TreeNode root) {
if (root == null) {
return;
}
Stack<TreeNode> stack1 = new Stack<>();
Stack<TreeNode> stack2 = new Stack<>();
stack1.push(root);
while (!stack1.isEmpty()) {
TreeNode node = stack1.pop();
stack2.push(node);
if (node.left!= null) {
stack1.push(node.left);
}
if (node.right!= null) {
stack1.push(node.right);
}
}
while (!stack2.isEmpty()) {
System.out.print(stack2.pop().val + " ");
}
}
}
(三)二叉树的层序遍历
按层次遍历二叉树,即从根节点开始,依次遍历每一层的节点。
Java 实现:
import java.util.LinkedList;
import java.util.Queue;
public class LevelOrderTraversal {
public static void levelOrder(TreeNode root) {
if (root == null) {
return;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
System.out.print(node.val + " ");
if (node.left!= null) {
queue.add(node.left);
}
if (node.right!= null) {
queue.add(node.right);
}
}
System.out.println();
}
}
}
(四)二叉搜索树、平衡二叉树、红黑树、哈夫曼树、前缀树的理解
-
二叉搜索树(Binary Search Tree):
- 定义:二叉搜索树是一种特殊的二叉树,对于树中的每个节点,其左子树中的所有节点的值都小于该节点的值,右子树中的所有节点的值都大于该节点的值。
- 特点:
- 中序遍历二叉搜索树可以得到一个有序的序列。
- 查找、插入和删除操作的时间复杂度在平均情况下为 O (log n),但在最坏情况下可能会退化为 O (n),当二叉搜索树退化为一条链时就会出现这种情况。
- 用途:常用于实现动态集合的数据结构,如集合的插入、删除、查找等操作。
-
平衡二叉树(Balanced Binary Tree):
- 定义:平衡二叉树是一种特殊的二叉树,它的左右子树的高度差不超过 1。
- 特点:
- 保持树的平衡可以确保查找、插入和删除操作的时间复杂度始终保持在 O (log n)。
- 常见的平衡二叉树有 AVL 树等。
- 用途:在需要高效进行查找、插入和删除操作,并且数据量较大的情况下使用。
-
红黑树(Red-Black Tree):
- 定义:红黑树是一种自平衡的二叉搜索树,每个节点都带有颜色属性(红色或黑色),并满足一定的性质。
- 特点:
- 从根节点到叶子节点的最长路径不超过最短路径的两倍。
- 插入和删除操作可能会导致树的不平衡,但可以通过旋转和重新着色等操作来保持树的平衡。
- 用途:广泛应用于各种需要高效查找、插入和删除操作的场景,如 C++ 的 STL 中的关联容器、Java 的 TreeMap 和 TreeSet 等。
-
哈夫曼树(Huffman Tree):
- 定义:哈夫曼树是一种带权路径长度最短的二叉树,也称为最优二叉树。
- 特点:
- 通过构建哈夫曼树,可以实现数据的高效压缩。
- 构建哈夫曼树的过程是根据字符的出现频率来确定字符的编码长度,出现频率高的字符编码长度短,出现频率低的字符编码长度长。
- 用途:主要用于数据压缩领域。
-
前缀树(Trie):
- 定义:前缀树也叫字典树,是一种用于快速检索字符串的数据结构。
- 特点:
- 对于一组字符串,前缀树可以快速判断一个字符串是否在这组字符串中,以及查找具有特定前缀的所有字符串。
- 节点通常包含字符和一个标志位,表示是否是一个完整的字符串。
- 用途:广泛应用于字符串匹配、自动补全、拼写检查等场景。
以下是用 Java 实现一个简单的二叉搜索树:
class BinarySearchTree {
private class Node {
int value;
Node left;
Node right;
Node(int value) {
this.value = value;
}
}
private Node root;
public void insert(int value) {
root = insert(root, value);
}
private Node insert(Node node, int value) {
if (node == null) {
return new Node(value);
}
if (value < node.value) {
node.left = insert(node.left, value);
} else if (value > node.value) {
node.right = insert(node.right, value);
}
return node;
}
public boolean search(int value) {
return search(root, value);
}
private boolean search(Node node, int value) {
if (node == null) {
return false;
}
if (value == node.value) {
return true;
} else if (value < node.value) {
return search(node.left, value);
} else {
return search(node.right, value);
}
}
}
更多推荐
所有评论(0)