java数据结构与算法面试题汇总

目录

数据结构与算法面试题汇总

一、排序算法

(一)快速排序

(二)归并排序

(三)冒泡排序

(四)插入排序

二、数据结构实现

(一)LRU 缓存

(二)哈希表

(三)基于数组的栈

(四)基于优先队列的最小堆

三、链表操作

(一)反转单链表

(二)找出两个单链表的交点

(三)检测单链表中是否有环

四、树的操作

(一)判断二叉树是否平衡

(二)二叉树的遍历

(三)二叉树的层序遍历

(四)二叉搜索树、平衡二叉树、红黑树、哈夫曼树、前缀树的理解


一、排序算法

(一)快速排序

快速排序是一种高效的排序算法,它采用分治的思想,通过选择一个基准元素,将数组分为两部分,小于基准元素的部分和大于基准元素的部分,然后分别对这两部分进行递归排序。

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;
    }
}

(二)二叉树的遍历

  1. 前序遍历:先访问根节点,然后遍历左子树,最后遍历右子树。
  2. 中序遍历:先遍历左子树,然后访问根节点,最后遍历右子树。
  3. 后序遍历:先遍历左子树,然后遍历右子树,最后访问根节点。

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();
        }
    }
}

(四)二叉搜索树、平衡二叉树、红黑树、哈夫曼树、前缀树的理解

  1. 二叉搜索树(Binary Search Tree)

    • 定义:二叉搜索树是一种特殊的二叉树,对于树中的每个节点,其左子树中的所有节点的值都小于该节点的值,右子树中的所有节点的值都大于该节点的值。
    • 特点:
      • 中序遍历二叉搜索树可以得到一个有序的序列。
      • 查找、插入和删除操作的时间复杂度在平均情况下为 O (log n),但在最坏情况下可能会退化为 O (n),当二叉搜索树退化为一条链时就会出现这种情况。
    • 用途:常用于实现动态集合的数据结构,如集合的插入、删除、查找等操作。
  2. 平衡二叉树(Balanced Binary Tree)

    • 定义:平衡二叉树是一种特殊的二叉树,它的左右子树的高度差不超过 1。
    • 特点:
      • 保持树的平衡可以确保查找、插入和删除操作的时间复杂度始终保持在 O (log n)。
      • 常见的平衡二叉树有 AVL 树等。
    • 用途:在需要高效进行查找、插入和删除操作,并且数据量较大的情况下使用。
  3. 红黑树(Red-Black Tree)

    • 定义:红黑树是一种自平衡的二叉搜索树,每个节点都带有颜色属性(红色或黑色),并满足一定的性质。
    • 特点:
      • 从根节点到叶子节点的最长路径不超过最短路径的两倍。
      • 插入和删除操作可能会导致树的不平衡,但可以通过旋转和重新着色等操作来保持树的平衡。
    • 用途:广泛应用于各种需要高效查找、插入和删除操作的场景,如 C++ 的 STL 中的关联容器、Java 的 TreeMap 和 TreeSet 等。
  4. 哈夫曼树(Huffman Tree)

    • 定义:哈夫曼树是一种带权路径长度最短的二叉树,也称为最优二叉树。
    • 特点:
      • 通过构建哈夫曼树,可以实现数据的高效压缩。
      • 构建哈夫曼树的过程是根据字符的出现频率来确定字符的编码长度,出现频率高的字符编码长度短,出现频率低的字符编码长度长。
    • 用途:主要用于数据压缩领域。
  5. 前缀树(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);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值