2024-08-26

以下是针对题目中给出的两个经典链表操作的Java代码示例:

  1. 反转单向链表



class ListNode {
    int val;
    ListNode next;
 
    ListNode(int x) {
        val = x;
        next = null;
    }
}
 
public class ReverseLinkedList {
    public 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;
    }
}
  1. 判断链表是否有环



class ListNode {
    int val;
    ListNode next;
 
    ListNode(int x) {
        val = x;
        next = null;
    }
}
 
public class LinkedListCycle {
    public boolean hasCycle(ListNode head) {
        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;
    }
}

这两个示例分别展示了如何反转单向链表和如何检测链表是否有环。这些操作在链表的实际应用中非常常见,并且是链表操作面试题的基础。

2024-08-26



public class SortAlgorithms {
 
    // 交换数组中的两个元素
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
 
    // 直接插入排序
    public static void insertionSort(int[] arr) {
        for (int i = 1; i < arr.length; i++) {
            for (int j = i; j > 0; j--) {
                if (arr[j] < arr[j - 1]) {
                    swap(arr, j, j - 1);
                }
            }
        }
    }
 
    // 冒泡排序
    public static void bubbleSort(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    swap(arr, j, j + 1);
                }
            }
        }
    }
 
    // 选择排序
    public static void selectionSort(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] < arr[minIndex]) {
                    minIndex = j;
                }
            }
            swap(arr, i, minIndex);
        }
    }
 
    // 快速排序
    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++;
 
                // 交换 arr[i] 和 arr[j]
                swap(arr, i, j);
            }
        }
        
        // 最后将枢纽元素放到正确的位置
        swap(arr, i + 1, high);
        
        return i + 1;
    }
 
    // 归并排序
    public static void mergeSort(int[] arr) {
        int mid = arr.length / 2;
        if (arr.length >= 2) {
            // 分割数组
            int[] leftHalf = Arrays.copyOfRange(arr, 0, mid);
            int[] rightHalf = Arrays.copyOfRange(arr, mid, arr.length);
 
            // 递归分割
            mergeSort(leftHalf);
            mergeSort(rightHalf);
 
            // 合并数组
  
2024-08-26

在Java中,可以通过创建一个二叉树的节点类来表示二叉树。以下是一个简单的二叉树节点类和一个示例方法,用于创建和打印一个二叉树。




class TreeNode {
    int value;
    TreeNode left;
    TreeNode right;
 
    TreeNode(int value) {
        this.value = value;
        this.left = null;
        this.right = null;
    }
}
 
public class BinaryTreeExample {
    public static void main(String[] args) {
        // 创建二叉树
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(3);
        root.left.left = new TreeNode(4);
        root.left.right = new TreeNode(5);
 
        // 打印二叉树
        printTree(root);
    }
 
    public static void printTree(TreeNode node) {
        if (node == null) {
            return;
        }
        printTree(node.left);
        System.out.println(node.value);
        printTree(node.right);
    }
}

这段代码定义了一个TreeNode类来表示二叉树中的节点,并在main方法中创建了一个具有特定结构的二叉树。printTree方法是一个递归方法,用于按层次遍历二叉树并打印每个节点的值。

2024-08-26

这个问题涉及到很多内容,包括七大排序算法(插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序、基数排序),以及计数排序和桶排序。由于篇幅所限,我将提供每种排序算法的核心思想和Java代码实现。

  1. 插入排序

插入排序的核心思想是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

Java代码实现:




public void insertionSort(int[] arr) {
    int i, j, key;
    for (i = 1; i < arr.length; i++) {
        key = arr[i];
        j = i - 1;
 
        // Move elements of arr[0..i-1], that are greater than key,
        // to one position ahead of their current position
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j = j - 1;
        }
        arr[j + 1] = key;
    }
}
  1. 快速排序

快速排序使用分治策略,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据要小,然后再按此方法对这两部分数据分别进行快速排序。

Java代码实现:




public 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 int partition(int[] arr, int low, int high) {
    // 选择一个枢纽值,通常是数组的第一个元素
    int pivot = arr[low];
 
    while (low < high) {
        // 在右边找到第一个小于枢纽值的元素
        while (low < high && arr[high] >= pivot) {
            high--;
        }
        arr[low] = arr[high]; // 将找到的元素放到左边
 
        // 在左边找到第一个大于枢纽值的元素
        while (low < high && arr[low] <= pivot) {
            low++;
        }
        arr[high] = arr[low]; // 将找到的元素放到右边
    }
 
    // 最终将枢纽值放到低位(左边的高位)
    arr[low] = pivot;
 
    // 返回枢纽位置
    return low;
}
  1. 计数排序

计数排序是一种稳定的线性时间排序算法,适用于一定范围内的整数排序。

Java代码实现:




public void countingSort(int[] arr) {
    // 找出数组中的最大值和最小值
    int max = arr[0];
    int min = arr[0];
    for (int i = 1; i < arr.length; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
        if (arr[i] < min) {
            min = arr[i];
        }
    }
 
    // 初始化计数数组
    int[] countArray = new int[max - min + 1];
 
    // 计数
    for (int i = 0; i < arr.length; i++) {
2024-08-26

并查集是一种树型的数据结构,用于处理一些不交集的合并及查询问题。它主要支持两种操作:

  1. union(x, y):把元素 x 和元素 y 所在的集合合并,要求 x 和 y 所在的集合不相交。
  2. isConnected(x, y):判断元素 x 和元素 y 是否属于同一个集合。

并查集可以用一个数组来实现,数组中的每个元素都存储了它的父节点的信息。如果一个元素的父节点是它自己,说明它是一个集合的代表元素,也就是根节点。

下面是一个简单的并查集的实现:




public class UnionFind {
    private int[] parent; // 记录每个节点的父节点
 
    public UnionFind(int size) {
        parent = new int[size];
        for (int i = 0; i < size; i++) {
            parent[i] = i; // 初始时每个元素的父节点是自己
        }
    }
 
    // 查找元素x的根节点
    private int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]); // 路径压缩
        }
        return parent[x];
    }
 
    // 合并元素x和元素y所在的集合
    public void union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX != rootY) {
            parent[rootX] = rootY; // 将一个集合的根节点连接到另一个集合的根节点
        }
    }
 
    // 判断元素x和元素y是否属于同一个集合
    public boolean isConnected(int x, int y) {
        return find(x) == find(y);
    }
}

使用方法:




UnionFind uf = new UnionFind(10); // 初始化并查集,有10个元素
uf.union(0, 1); // 合并元素0和元素1所在的集合
uf.union(2, 3);
boolean connected = uf.isConnected(0, 2); // 判断元素0和元素2是否属于同一个集合

这个简单的实现支持路径压缩,即在find操作中,我们会把路径上的每个节点直接连接到根节点,以减少查找时间。

2024-08-26

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

哈希表有多种不同的实现方式,但是在所有的实现中,都需要解决以下两个核心问题:

  1. 哈希码的计算:如何为一个对象计算其哈希码?
  2. 碰撞处理:如果两个对象的哈希码相同,该如何处理?

在Java中,每个对象都有自己的hashCode()方法,用于返回该对象的哈希码。哈希码是一个用于表示对象身份的整数,在哈希表中,相同对象的哈希码应该是相同的。

HashMap和HashSet都是基于哈希表的数据结构,HashMap用于存储键值对,HashSet用于存储不重复的元素。

  1. HashMap

    HashMap内部是用一个数组来存放元素,元素存放的位置由键值对的键的哈希码决定。如果两个键的哈希码相同,那么它们会被放在数组的同一个位置,并形成一个链表。




HashMap<Integer, String> map = new HashMap<>();
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");
  1. HashSet

    HashSet内部也是用一个数组来存放元素,元素存放的位置由元素自身的哈希码决定。如果两个元素的哈希码相同,那么它们会被放在数组的同一个位置,并形成一个链表。




HashSet<String> set = new HashSet<>();
set.add("One");
set.add("Two");
set.add("Three");

在Java中,对象的equals方法和hashCode方法都是配套使用的。当我们重写一个对象的equals方法时,通常需要重写其hashCode方法,以保证当两个对象通过equals方法比较返回true时,它们的hashCode值也应该相同。




public class Person {
    private int id;
    private String name;
 
    // 构造方法、getter和setter省略
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return id == person.id &&
               Objects.equals(name, person.name);
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

在上述的Person类中,我们重写了equals方法,用于比较两个Person对象是否相等,同时我们还重写了hashCode方法,用于返回该对象的哈希码。在hashCode方法中,我们使用了Objects类的hash方法,它可以为多个字段生成一个哈希码,这样可以确保当两个Person对象的id和name都相等时,它们的哈希码也是相等的。这样一来,当我们将Person对象放入HashSet或者作为HashMap的键时,可以更加高效地存储和检索数据。

2024-08-26

栈(Stack)是一种线性数据结构,它遵循后进先出(LIFO)原则。在计算机科学中,栈常用于存储临时数据,如函数调用的局部变量、保存中间结果的计算过程等。

以下是使用Java实现栈的基本操作:




public class Stack<T> {
    private int size;
    private Node<T> top;
 
    public Stack() {
        size = 0;
        top = null;
    }
 
    // 入栈操作
    public void push(T data) {
        Node<T> newNode = new Node<>(data);
        if (top != null) {
            newNode.next = top;
        }
        top = newNode;
        size++;
    }
 
    // 出栈操作
    public T pop() {
        if (isEmpty()) {
            return null;
        }
        T data = top.data;
        top = top.next;
        size--;
        return data;
    }
 
    // 查看栈顶元素
    public T peek() {
        if (isEmpty()) {
            return null;
        }
        return top.data;
    }
 
    // 判断栈是否为空
    public boolean isEmpty() {
        return size == 0;
    }
 
    // 获取栈的大小
    public int size() {
        return size;
    }
 
    private static class Node<T> {
        T data;
        Node<T> next;
 
        public Node(T data) {
            this.data = data;
            this.next = null;
        }
    }
}

使用示例:




public class StackExample {
    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();
 
        // 入栈
        stack.push(1);
        stack.push(2);
        stack.push(3);
 
        // 查看栈顶元素
        System.out.println("栈顶元素: " + stack.peek()); // 输出: 栈顶元素: 3
 
        // 出栈
        System.out.println("出栈元素: " + stack.pop()); // 输出: 出栈元素: 3
 
        // 再次查看栈顶元素
        System.out.println("栈顶元素: " + stack.peek()); // 输出: 栈顶元素: 2
 
        // 获取栈的大小
        System.out.println("栈的大小: " + stack.size()); // 输出: 栈的大小: 2
 
        // 判断栈是否为空
        System.out.println("栈是否为空: " + stack.isEmpty()); // 输出: 栈是否为空: false
    }
}

以上代码实现了一个简单的栈数据结构,并展示了如何使用它进行入栈、出栈、查看栈顶元素等操作。这个实现有利于进一步理解栈的概念和操作。

2024-08-26



<template>
  <div class="selection-sort-animation">
    <div class="animation-container">
      <div
        v-for="(item, index) in items"
        :key="index"
        class="animation-bar"
        :style="{ height: `${item}px`, backgroundColor: getColor(index) }"
      ></div>
    </div>
    <button @click="startAnimation">排序</button>
  </div>
</template>
 
<script>
export default {
  data() {
    return {
      items: [...Array(10)].map(() => Math.random() * 100), // 初始化10个高度随机的方块
      sortedItems: [], // 用于存放排序后的方块数组
      sorting: false, // 是否正在进行排序
    };
  },
  methods: {
    getColor(index) {
      return this.sortedItems.includes(index) ? 'green' : 'blue';
    },
    startAnimation() {
      if (this.sorting) return; // 如果已经在排序,则不再执行
      this.sorting = true;
      this.sortedItems = []; // 重置排序记录数组
      const sort = () => {
        if (this.items.length <= 1) {
          this.sorting = false;
          return;
        }
        const index = this.findSmallest(this.items);
        const smallest = this.items.splice(index, 1)[0];
        this.sortedItems.push(index);
        setTimeout(() => {
          this.items.unshift(smallest);
          sort();
        }, 1000);
      };
      sort();
    },
    findSmallest(arr) {
      let smallest = arr[0];
      let index = 0;
      for (let i = 1; i < arr.length; i++) {
        if (arr[i] < smallest) {
          smallest = arr[i];
          index = i;
        }
      }
      return index;
    },
  },
};
</script>
 
<style scoped>
.animation-container {
  display: flex;
}
.animation-bar {
  margin: 5px;
  transition: all 0.5s;
}
</style>

这段代码实现了选择排序动画的初始化和触发。它首先在data中初始化了一个包含随机高度的方块数组,并定义了一个空数组来记录已排序的方块。在methods中定义了getColor方法来根据方块是否已排序改变颜色,以及startAnimation方法来开始排序动画过程。startAnimation方法中定义了选择排序的逻辑,并通过setTimeout模拟动画效果。这个例子展示了如何在Vue中结合JavaScript实现动画效果,并且是排序算法可视化教学的一个很好的起点。

2024-08-26



package main
 
import (
    "fmt"
)
 
// 定义一个User类型,它是由一个名字(Name)和年龄(Age)组成的结构体
type User struct {
    Name string
    Age  int
}
 
func main() {
    // 创建User类型的变量并初始化
    user1 := User{"Alice", 30}
    user2 := User{"Bob", 25}
 
    // 打印用户信息
    fmt.Printf("User1: %v; User2: %v\n", user1, user2)
}

这段代码定义了一个User结构体,并创建了两个实例user1user2。然后,它使用fmt.Printf打印出这些用户的信息。这是Go语言中类型定义和结构体使用的基本示例,适合初学者学习和理解。

在Elasticsearch中,数据是按照一定的数据结构进行存储的,其中最基本的数据结构是段(segment)。段是Elasticsearch中一个非常重要的概念,它是将文档(document)存储于文件系统的一种方式。

一个段是一个不可变的,一组由文档组成的 Postings 列表,它是由一个或多个文档组成的列表,每个文档都有一个包含它的词项(terms)的倒排列表。

在段中,倒排列表是基于文档的,其中包含了词项到文档ID的映射。这样可以快速找到包含特定词项的所有文档。

以下是一个简单的段结构示例:




Segment:
    Document1:
        Term1 -> Posting List
        Term3 -> Posting List
    Document2:
        Term2 -> Posting List
        Term3 -> Posting List
    ...

在这个例子中,每个文档都有一个倒排列表,列出了所有在该文档中出现的词项及其位置信息。这样就可以快速找到包含特定词项的所有文档。

这种结构是Elasticsearch实现快速全文搜索的关键,它允许Elasticsearch在几毫秒内返回成千上万的搜索结果。

注意,段是不可变的,这意味着在段创建后,它不能被更新或删除。更新或删除文档时,Elasticsearch 会创建一个新的段,其中包含更新或删除后的文档。旧的段将在后台合并以释放空间。这种机制称为乐观并发控制(optimistic concurrency control)。