深入了解数据结构中的查找算法

目录

前言

1. 查找的基本概念

2. 顺序查找和折半查找

2.1 顺序查找

2.2 折半查找

2.3分块查找

3. 树形查找

3.1 二叉搜索树 (BST)

3.2平衡二叉树

3.3红黑树

4. B 树和 B+ 树

4.1 B 树

4.2 B+ 树

5. 哈希表 (Hash Table)

5.1 基本操作

5.2 实现

5.3 复杂性分析

5.4 实际应用

结论


前言

        在计算机科学中,查找算法是一种用于在数据结构中定位所需数据的过程。查找算法在各种应用程序中都非常重要,例如数据库搜索、符号表查找和路由。本文将全面介绍数据结构中的查找算法,涵盖基本概念、常见算法及其实现,并探讨一些高级数据结构的查找技术。

1. 查找的基本概念

在讨论特定的查找算法之前,让我们了解一些基本概念和术语。

  • 键(Key):要查找的数据项。例如,在搜索员工记录时,员工 ID 或姓名可能是键。
  • 记录(Record):包含键和其他相关信息的数据项。
  • 数据结构:存储记录的结构化集合。常见的数据结构包括数组、链表和树。
  • 查找操作:在数据结构中定位具有给定键的记录的过程。
  • 成功查找:如果数据结构中存在具有给定键的记录,则查找操作成功。
  • 不成功查找:如果数据结构中不存在具有给定键的记录,则查找操作不成功。
  • 查找效率:衡量查找算法的效率通常使用两个指标:查找时间复杂性和空间复杂性。时间复杂性表示执行查找操作所需的时间,而空间复杂性表示存储数据结构所需的额外空间。

2. 顺序查找和折半查找

2.1 顺序查找

        顺序查找是最基本的查找算法之一。它涉及到顺序遍历数据结构中的每个记录,直到找到具有给定键的记录或遍历完所有记录。顺序查找适用于未排序或随机排序的数据结构。

实现

        假设我们有一个存储员工记录的数组,每个记录包含员工 ID 和姓名。我们可以使用顺序查找来查找具有给定员工 ID 的记录。

public class SequentialSearch {public static Employee sequentialSearch(int[] ids, String[] names, int key) {for (int i = 0; i < ids.length; i++) {if (ids[i] == key) {return new Employee(ids[i], names[i]);}}return null; // 不成功查找}// Employee 类用于表示员工记录static class Employee {private int id;private String name;public Employee(int id, String name) {this.id = id;this.name = name;}// 省略 getter 和 setter}public static void main(String[] args) {int[] ids = {101, 102, 103, 104, 105};String[] names = {"Alice", "Bob", "Charlie", "David", "Eve"};int key = 103;Employee employee = sequentialSearch(ids, names, key);if (employee != null) {System.out.println("Employee found: ID = " + employee.getId() + ", Name = " + employee.getName());} else {System.out.println("Employee not found.");}}
}

复杂性分析

  • 时间复杂性:O(n),其中 n 是数据结构中的记录数。顺序查找在最坏情况下需要检查所有记录。
  • 空间复杂性:O(1),因为它不需要额外的空间。

实际应用

        顺序查找适用于小型数据集或未排序的数据结构。它还可以在查找操作不频繁且数据结构经常变化的情况下使用。

2.2 折半查找

        折半查找,也称为二分查找,是一种适用于有序数据结构的查找算法。它通过不断缩小搜索范围来工作,直到找到所需的记录或确定记录不存在。

实现

        让我们使用相同的员工记录数组来实现折半查找。

public class BinarySearch {public static Employee binarySearch(int[] ids, String[] names, int key) {int left = 0;int right = ids.length - 1;while (left <= right) {int mid = left + (right - left) / 2;if (ids[mid] == key) {return new Employee(ids[mid], names[mid]);} else if (ids[mid] < key) {left = mid + 1;} else {right = mid - 1;}}return null; // 不成功查找}// Employee 类与顺序查找中相同public static void main(String[] args) {int[] ids = {101, 102, 103, 104, 105};String[] names = {"Alice", "Bob", "Charlie", "David", "Eve"};sortArrays(ids, names); // 确保数组有序int key = 103;Employee employee = binarySearch(ids, names, key);if (employee != null) {System.out.println("Employee found: ID = " + employee.getId() + ", Name = " + employee.getName());} else {System.out.println("Employee not found.");}}// 对 ids 和 names 数组进行排序private static void sortArrays(int[] ids, String[] names) {Arrays.sort(ids);Arrays.sort(names);}
}

复杂性分析

  • 时间复杂性:平均和最好情况下为 O(log n),其中 n 是数据结构中的记录数。折半查找通过每次将搜索范围减半来工作,因此具有对数时间复杂性。
  • 空间复杂性:O(1),因为它不需要额外的空间。

实际应用

        折半查找适用于有序数据结构,当数据集较大且需要高效的查找操作时,它非常有用。例如,在实现字典或电话簿查找时可以使用折半查找。

2.3分块查找

        分块查找(Block Search)是一种查找算法,适用于在一个有序表中进行查找操作。该算法将有序表分成若干个块(或称为区间),每个块中包含一定数量的元素。这些块本身也按照一定的顺序排列。

        分块查找的基本思想是首先对块内的元素进行顺序查找,确定待查找元素可能所在的块,然后再在该块内进行二分查找或顺序查找,从而缩小查找范围,最终找到目标元素或者确定目标元素不存在。

        分块查找的主要优点在于能够利用数据的局部性,减少查找的范围,从而提高查找效率。它在某些场景下比纯粹的顺序查找更加高效,尤其是当数据量较大且分布较为均匀时。

实现

import java.util.ArrayList;
import java.util.Collections;public class BlockSearch {// 分块查找函数public static int blockSearch(int[] arr, int key, int blockSize) {// 每块的大小应该小于等于数组的长度if (blockSize <= 0 || blockSize > arr.length) {return -1; // 错误的 blockSize}// 先对数组进行排序ArrayList<Integer> sortedArr = new ArrayList<>();for (int value : arr) {sortedArr.add(value);}Collections.sort(sortedArr);// 计算块数int numOfBlocks = (int) Math.ceil(arr.length / (double) blockSize);// 在每个块中执行顺序查找for (int i = 0; i < numOfBlocks; i++) {int startIdx = i * blockSize;int endIdx = Math.min((i + 1) * blockSize, arr.length);if (sortedArr.get(startIdx) <= key && key <= sortedArr.get(endIdx - 1)) {// 在当前块中进行顺序查找for (int j = startIdx; j < endIdx; j++) {if (sortedArr.get(j) == key) {return j;}}return -1; // 在当前块中未找到}}return -1; // 在所有块中未找到}public static void main(String[] args) {int[] arr = {4, 2, 8, 1, 9, 5, 7, 3, 6}; // 未排序的数组int key = 5;int blockSize = 3;int index = blockSearch(arr, key, blockSize);if (index != -1) {System.out.println("元素 " + key + " 在数组中的索引为: " + index);} else {System.out.println("元素 " + key + " 未在数组中找到");}}
}

 复杂性分析

  1. 时间复杂性

    • 块内顺序查找的时间复杂度为 O(blockSize),因为每个块内的元素数量不超过 blockSize,所以在最坏情况下需要遍历整个块。
    • 块的数量为数组长度除以块的大小,即 O(n/blockSize),其中 n 是数组的长度。
    • 在块内进行顺序查找的时间复杂度为 O(blockSize),最坏情况下需要遍历一个块的所有元素。
    • 因此,分块查找的时间复杂度为 O(n/blockSize * blockSize) = O(n),其中 n 是数组的长度。
  2. 空间复杂度:

    • 分块查找的空间复杂度主要取决于排序后的数组和额外的空间用于存储排序后的数组。
    • 排序后的数组占用 O(n) 的空间。
    • 因此,分块查找的空间复杂度为 O(n)。

3. 树形查找

        树形查找算法利用树形数据结构的层次结构来实现高效的查找操作。二叉搜索树 (Binary Search Tree, BST) 是最常见的树形查找数据结构之一。

3.1 二叉搜索树 (BST)

        二叉搜索树是一种树形数据结构,其中每个节点都有两个子节点:左子节点和右子节点。左子节点包含小于父节点的键,而右子节点包含大于父节点的键。这允许在对数时间内进行查找操作。

实现

        让我们使用二叉搜索树来实现员工记录的查找。

public class BinarySearchTreeSearch {static class TreeNode {int id;String name;TreeNode left, right;public TreeNode(int id, String name) {this.id = id;this.name = name;this.left = null;this.right = null;}}public static Employee search(TreeNode root, int key) {if (root == null) {return null;}if (root.id == key) {return new Employee(root.id, root.name);} else if (root.id < key) {return search(root.right, key);} else {return search(root.left, key);}}// Employee 类与顺序查找中相同public static void main(String[] args) {TreeNode root = new TreeNode(103, "Charlie");root.left = new TreeNode(101, "Alice");root.right = new TreeNode(105, "Eve");root.left.left = new TreeNode(102, "Bob");root.left.right = new TreeNode(104, "David");int key = 104;Employee employee = search(root, key);if (employee != null) {System.out.println("Employee found: ID = " + employee.getId() + ", Name = " + employee.getName());} else {System.out.println("Employee not found.");}}
}

复杂性分析

  • 时间复杂性:平均和最好情况下为 O(log n),其中 n 是数据结构中的记录数。二叉搜索树的查找时间取决于树的高度。对于平衡树,查找时间接近对数时间。
  • 空间复杂性:O(1),因为它不需要额外的空间。

实际应用

二叉搜索树适用于需要高效插入、删除和查找操作的场景。例如,在实现动态集合或数据库索引时可以使用二叉搜索树。

3.2平衡二叉树

        平衡二叉树(Balanced Binary Tree)是一种特殊的二叉搜索树,其主要特点是保持了树的平衡性,即任意节点的左右子树高度差不超过 1。这种平衡性确保了树的高度相对较小,使得查找、插入和删除等操作的时间复杂度能够保持在 O(log n) 的水平。

实现

class Node {int key;Node left, right;int height;Node(int value) {key = value;height = 1;}
}public class AVLTree {Node root;int height(Node node) {if (node == null)return 0;return node.height;}int max(int a, int b) {return (a > b) ? a : b;}Node rightRotate(Node y) {Node x = y.left;Node T2 = x.right;x.right = y;y.left = T2;y.height = max(height(y.left), height(y.right)) + 1;x.height = max(height(x.left), height(x.right)) + 1;return x;}Node leftRotate(Node x) {Node y = x.right;Node T2 = y.left;y.left = x;x.right = T2;x.height = max(height(x.left), height(x.right)) + 1;y.height = max(height(y.left), height(y.right)) + 1;return y;}int getBalance(Node node) {if (node == null)return 0;return height(node.left) - height(node.right);}Node insert(Node node, int key) {if (node == null)return (new Node(key));if (key < node.key)node.left = insert(node.left, key);else if (key > node.key)node.right = insert(node.right, key);elsereturn node;node.height = 1 + max(height(node.left), height(node.right));int balance = getBalance(node);if (balance > 1 && key < node.left.key)return rightRotate(node);if (balance < -1 && key > node.right.key)return leftRotate(node);if (balance > 1 && key > node.left.key) {node.left = leftRotate(node.left);return rightRotate(node);}if (balance < -1 && key < node.right.key) {node.right = rightRotate(node.right);return leftRotate(node);}return node;}void preOrder(Node node) {if (node != null) {System.out.print(node.key + " ");preOrder(node.left);preOrder(node.right);}}public static void main(String[] args) {AVLTree tree = new AVLTree();tree.root = tree.insert(tree.root, 10);tree.root = tree.insert(tree.root, 20);tree.root = tree.insert(tree.root, 30);tree.root = tree.insert(tree.root, 40);tree.root = tree.insert(tree.root, 50);tree.root = tree.insert(tree.root, 25);System.out.println("Preorder traversal of constructed AVL tree is:");tree.preOrder(tree.root);}
}

3.3红黑树

红黑树(Red-Black Tree)是一种自平衡的二叉搜索树,它具有以下特性:

  1. 每个节点要么是红色,要么是黑色。
  2. 根节点是黑色的。
  3. 每个叶子节点(NIL 节点)是黑色的。
  4. 如果一个节点是红色的,则它的两个子节点都是黑色的(即不存在两个相邻的红色节点)。
  5. 从任一节点到其每个叶子的所有路径都包含相同数量的黑色节点(黑色高度相同)。

        这些特性保证了红黑树的关键性质,即从根节点到叶子节点的最长路径不超过最短路径的两倍,因此红黑树的高度为 O(log n),其中 n 是树中节点的数量。

实现

class Node {int data;Node parent;Node left;Node right;int color; // 0 代表黑色,1 代表红色public Node(int data) {this.data = data;}
}public class RedBlackTree {private Node root;private Node TNULL;// 先序遍历private void preOrderHelper(Node node) {if (node != TNULL) {System.out.print(node.data + " ");preOrderHelper(node.left);preOrderHelper(node.right);}}// 中序遍历private void inOrderHelper(Node node) {if (node != TNULL) {inOrderHelper(node.left);System.out.print(node.data + " ");inOrderHelper(node.right);}}// 后序遍历private void postOrderHelper(Node node) {if (node != TNULL) {postOrderHelper(node.left);postOrderHelper(node.right);System.out.print(node.data + " ");}}// 查找树中的节点private Node searchTreeHelper(Node node, int key) {if (node == TNULL || key == node.data) {return node;}if (key < node.data) {return searchTreeHelper(node.left, key);}return searchTreeHelper(node.right, key);}// 删除节点后平衡树private void fixDelete(Node x) {Node s;while (x != root && x.color == 0) {if (x == x.parent.left) {s = x.parent.right;if (s.color == 1) {s.color = 0;x.parent.color = 1;leftRotate(x.parent);s = x.parent.right;}if (s.left.color == 0 && s.right.color == 0) {s.color = 1;x = x.parent;} else {if (s.right.color == 0) {s.left.color = 0;s.color = 1;rightRotate(s);s = x.parent.right;}s.color = x.parent.color;x.parent.color = 0;s.right.color = 0;leftRotate(x.parent);x = root;}} else {s = x.parent.left;if (s.color == 1) {s.color = 0;x.parent.color = 1;rightRotate(x.parent);s = x.parent.left;}if (s.right.color == 0 && s.right.color == 0) {s.color = 1;x = x.parent;} else {if (s.left.color == 0) {s.right.color = 0;s.color = 1;leftRotate(s);s = x.parent.left;}s.color = x.parent.color;x.parent.color = 0;s.left.color = 0;rightRotate(x.parent);x = root;}}}x.color = 0;}private void rbTransplant(Node u, Node v) {if (u.parent == null) {root = v;} else if (u == u.parent.left) {u.parent.left = v;} else {u.parent.right = v;}v.parent = u.parent;}private void deleteNodeHelper(Node node, int key) {Node z = TNULL;Node x, y;while (node != TNULL) {if (node.data == key) {z = node;}if (node.data <= key) {node = node.right;} else {node = node.left;}}if (z == TNULL) {System.out.println("树中找不到该键");return;}y = z;int yOriginalColor = y.color;if (z.left == TNULL) {x = z.right;rbTransplant(z, z.right);} else if (z.right == TNULL) {x = z.left;rbTransplant(z, z.left);} else {y = minimum(z.right);yOriginalColor = y.color;x = y.right;if (y.parent == z) {x.parent = y;} else {rbTransplant(y, y.right);y.right = z.right;y.right.parent = y;}rbTransplant(z, y);y.left = z.left;y.left.parent = y;y.color = z.color;}if (yOriginalColor == 0) {fixDelete(x);}}// 插入节点后平衡树private void fixInsert(Node k) {Node u;while (k.parent.color == 1) {if (k.parent == k.parent.parent.right) {u = k.parent.parent.left; // 叔节点if (u.color == 1) {u.color = 0;k.parent.color = 0;k.parent.parent.color = 1;k = k.parent.parent;} else {if (k == k.parent.left) {k = k.parent;rightRotate(k);}k.parent.color = 0;k.parent.parent.color = 1;leftRotate(k.parent.parent);}} else {u = k.parent.parent.right; // 叔节点if (u.color == 1) {u.color = 0;k.parent.color = 0;k.parent.parent.color = 1;k = k.parent.parent;} else {if (k == k.parent.right) {k = k.parent;leftRotate(k);}k.parent.color = 0;k.parent.parent.color = 1;rightRotate(k.parent.parent);}}if (k == root) {break;}}root.color = 0;}private void printHelper(Node root, String indent, boolean last) {if (root != TNULL) {System.out.print(indent);if (last) {System.out.print("R----");indent += "     ";} else {System.out.print("L----");indent += "|    ";}String sColor = root.color == 1 ? "红色" : "黑色";System.out.println(root.data + "(" + sColor + ")");printHelper(root.left, indent, false);printHelper(root.right, indent, true);}}public RedBlackTree() {TNULL = new Node(0);TNULL.color = 0;TNULL.left = null;TNULL.right = null;root = TNULL;}// 先序遍历public void preorder() {preOrderHelper(this.root);}// 中序遍历public void inorder() {inOrderHelper(this.root);}// 后序遍历public void postorder() {postOrderHelper(this.root);}// 查找树中的节点public Node searchTree(int k) {return searchTreeHelper(this.root, k);}// 查找最小节点public Node minimum(Node node) {while (node.left != TNULL) {node = node.left;}return node;}// 查找最大节点public Node maximum(Node node) {while (node.right != TNULL) {node = node.right;}return node;}// 左旋public void leftRotate(Node x) {Node y = x.right;x.right = y.left;if (y.left != TNULL) {y.left.parent = x;}y.parent = x.parent;if (x.parent == null) {this.root = y;} else if (x == x.parent.left) {x.parent.left = y;} else {x.parent.right = y;}y.left = x;x.parent = y;}// 右旋public void rightRotate(Node x) {Node y = x.left;x.left = y.right;if (y.right != TNULL) {y.right.parent = x;}y.parent = x.parent;if (x.parent == null) {this.root = y;} else if (x == x.parent.right) {x.parent.right = y;} else {x.parent.left = y;}y.right = x;x.parent = y;}// 插入键到树中的适当位置,并修复树public void insert(int key) {Node node = new Node(key);node.parent = null;node.data = key;node.left = TNULL;node.right = TNULL;node.color = 1; // 新节点必须是红色Node y = null;Node x = this.root;while (x != TNULL) {y = x;if (node.data < x.data) {x = x.left;} else {x = x.right;}}// y 是 x 的父节点node.parent = y;if (y == null) {root = node;} else if (node.data < y.data) {y.left = node;} else {y.right = node;}// 如果新节点是根节点,直接返回if (node.parent == null) {node.color = 0;return;}// 如果祖父节点为空,直接返回if (node.parent.parent == null) {return;}// 修复树fixInsert(node);}public Node getRoot() {return this.root;}// 从树中删除节点public void deleteNode(int data) {deleteNodeHelper(this.root, data);}// 在屏幕上打印树的结构public void prettyPrint() {printHelper(this.root, "", true);}public static void main(String[] args) {RedBlackTree bst = new RedBlackTree();bst.insert(55);bst.insert(40);bst.insert(65);bst.insert(60);bst.insert(75);bst.insert(57);System.out.println("构建的树的先序遍历结果:");bst.preorder();System.out.println("\n\n构建的树的中序遍历结果:");bst.inorder();System.out.println("\n\n构建的树的后序遍历结果:");bst.postorder();System.out.println("\n\n树的结构:");bst.prettyPrint();}
}

4. B 树和 B+ 树

当数据集变得非常大时,像二叉搜索树这样的简单树结构可能无法有效地处理查找操作。B 树和 B+ 树是专门为高效查找而设计的高级树形数据结构。

4.1 B 树

        B 树是一种自平衡的搜索树,具有多个键和多个子节点。每个节点可以包含多个键和指向子节点的指针。B 树通过维护平衡和限制节点大小来确保查找操作的高效性。

实现

        B 树的实现比二叉搜索树复杂得多,并且通常在数据库系统中实现。在本文中,我们将简要介绍 B 树的概念。

B 树具有以下属性:

  • 每个节点可以包含多个键和指向子节点的指针。
  • 根节点至少有两个键(除非它是叶子节点)。
  • 所有叶子节点位于同一层。
  • 所有节点(除根节点外)至少包含一个键。
  • 所有节点(除叶子节点外)至少有两个子节点。

        B 树的查找操作类似于二叉搜索树的查找。它涉及到从根节点开始的递归下降,直到找到所需的键或到达叶子节点。

4.2 B+ 树

        B+ 树是 B 树的变体,专门用于磁盘或数据库中的高效查找和检索。与 B 树不同,B+ 树的叶子节点包含所有键的链接列表,这使得范围查找变得更加高效。

B+ 树具有以下属性:

  • 每个节点可以包含多个键和指向子节点的指针。
  • 非叶子节点仅作为索引使用,实际数据存储在叶子节点中。
  • 所有内部节点都是完全填充的,这意味着每个节点都有最大允许数量的子节点。
  • 叶子节点形成一个有序链表,其中包含所有键。

复杂性分析

  • B 树和 B+ 树的时间复杂性为 O(log n),其中 n 是数据结构中的记录数。它们的高效性来自于平衡的树结构和节点中键的分布式存储。
  • 空间复杂性取决于 B 树或 B+ 树的实现细节,但通常比线性数据结构更高。

实际应用

        B 树和 B+ 树广泛应用于数据库系统中,用于实现高效的索引和检索。它们还用于实现文件系统、路由器和缓存系统等。

5. 哈希表 (Hash Table)

        哈希表是一种允许在平均情况下以常数时间进行插入和查找的数据结构。它使用散列函数将键映射到数组中的索引。

5.1 基本操作

哈希表包含以下基本操作:

  • 插入:将键/值对插入到哈希表中。如果数组中已经存在具有相同键的项,则将其覆盖。
  • 查找:使用键在哈希表中查找值。
  • 删除:删除具有给定键的项。

5.2 实现

让我们使用 Java 实现一个简单的哈希表。

public class HashTable {private static final int TABLE_SIZE = 10;private int[] keys;private String[] values;public HashTable() {keys = new int[TABLE_SIZE];values = new String[TABLE_SIZE];}public void insert(int key, String value) {int hash = hashFunction(key);if (keys[hash] == 0) {keys[hash] = key;values[hash] = value;} else {values[hash] = value; // 覆盖现有值}}public String search(int key) {int hash = hashFunction(key);if (keys[hash] == key) {return values[hash];}return null; // 不成功查找}private int hashFunction(int key) {return key % TABLE_SIZE;}public static void main(String[] args) {HashTable hashTable = new HashTable();hashTable.insert(101, "Alice");hashTable.insert(102, "Bob");hashTable.insert(103, "Charlie");System.out.println("Searching for key 102...");String value = hashTable.search(102);if (value != null) {System.out.println("Value found: " + value);} else {System.out.println("Key not found.");}}
}

5.3 复杂性分析

  • 时间复杂性:哈希表的插入和查找操作的平均时间复杂性为 O(1),即常数时间。
  • 空间复杂性:O(n),其中 n 是哈希表中键/值对的数量。

5.4 实际应用

       

        哈希(HASH)函数在计算机科学和信息安全领域有着广泛的实际运用。以下是几个常见的哈希函数的实际应用:

  1. 密码存储和验证:哈希函数常用于存储用户密码的安全性。当用户注册时,其密码会经过哈希函数进行加密处理,并将哈希值存储在数据库中。当用户登录时,输入的密码经过相同的哈希函数处理后,与数据库中存储的哈希值进行比对,而不是直接比对明文密码。这样做可以保护用户密码的安全,即使数据库被盗也无法轻易还原出原始密码。

  2. 数据完整性校验:哈希函数可用于验证数据的完整性,比如文件传输过程中的数据完整性校验。发送方使用哈希函数对文件内容进行哈希计算,并将哈希值一同发送给接收方。接收方在接收到文件后,再次对文件内容进行哈希计算,然后将计算得到的哈希值与发送方提供的哈希值进行比对。如果两个哈希值一致,则表明数据在传输过程中没有被篡改。

  3. 数据结构:哈希表是一种常见的数据结构,利用哈希函数将键映射到索引位置,实现了高效的数据存储和检索。在哈希表中,每个键都被哈希到唯一的索引位置,从而可以在常数时间内进行插入、删除和查找操作。

  4. 分布式存储和负载均衡:一致性哈希算法被广泛用于分布式系统中的负载均衡和数据分片。一致性哈希将数据映射到一个环形的哈希空间上,并将节点也映射到同样的空间上。通过这种方式,可以快速定位数据应该存储在哪个节点上,并在节点动态增减时尽量减少数据迁移的成本。

  5. 消息摘要和数字签名:哈希函数用于生成消息摘要,用于验证消息的完整性和真实性。数字签名算法通常涉及对消息的哈希值进行签名,以便接收方可以使用发送方的公钥验证消息的来源和完整性。

结论

        在本文中,我们探讨了数据结构中的各种查找算法,从基本的顺序查找和折半查找到高级的 B 树和哈希表。这些算法使我们能够有效地管理和检索大型数据集。

        查找算法在计算机科学中发挥着至关重要的作用。随着数据集的增长和应用程序需求的增加,高效的查找算法变得越来越重要。本文介绍的算法提供了在不同情况下处理查找操作的工具箱。

        在实际应用中,选择适当的查找算法取决于许多因素,包括数据集的大小、查找操作的频率和数据结构的性质。了解不同的查找算法及其优势和局限可以帮助开发人员和研究人员做出明智的决定,从而确保其应用程序的高效性和可扩展性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/4062.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

# IntelliJ IDEA 中 springboot 启动类 SpringApplication.run 报红分析

IntelliJ IDEA 中 springboot 启动类 SpringApplication.run 报红分析 一、原因分析&#xff1a;通常 SpringApplication.run 报红&#xff0c;可能是由以下几种原因造成的&#xff1a; 1、项目的主配置文件中 pom.xml 可能没有导入相关依赖。 2、方法参数错误&#xff1a;S…

全量知识系统 程序详细设计 库模式的存储库模型: “三生”(派生衍生自生) (QA SmartChat)

Q1. 请仔细说说 派生和衍生 各自的意思以及关系 派生和衍生是两个近义词&#xff0c;但在语境不同的情况下有着不同的含义。 派生通常指从某种事物或概念中产生出新的东西或概念。它可以用来表示从一个基本的概念或原始的实体中推导或演化出来的新的思想、理论、产品或方法。…

【C++例】虚函数—代码实例

1.虚函数的动态绑定 //虚函数&#xff08;非静态的成员函数&#xff09; //动态绑定&#xff1a;只有通过基类的指针或引用调用虚函数时 #include<iostream> using namespace std; class Base1 { public:virtual void display() const; }; // 如果把某个函数指定为final…

通用型自定义拼接 SQL 脚本,摆脱重复工作量

通用型自定义拼接sql脚本,摆脱重复工作量 在开发 Restful 接口时&#xff0c;我们经常需要根据前端传递的参数动态拼接 SQL 查询语句&#xff0c;以满足不同的查询需求。本文将介绍一个通用的自定义拼接 SQL 脚本的方法&#xff0c;帮助开发人员减少重复工作量。 首先&#x…

【C++打怪之路Lv3】-- 类和对象(上)

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;C打怪之路&#xff0c;python从入门到精通&#xff0c;数据结构&#xff0c;C语言&#xff0c;C语言题集&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文(平均质量分82)&#…

避坑:我找到了Hexo安知鱼的音乐馆配置(目前好像还没有教程)

我发现安知鱼的音乐配置根本就找不到说明就无奈自己逆向了一下&#xff0c;找到了配置文件&#xff0c;写在博客里记录一下也算是给大家避坑 我的版本是1.6.12&#xff0c;如果你和我的不一样可以看后面 配置文件就是Blog\themes\anzhiyu\source\js\utils.js 打开该文件&…

开发环境搭建:Windows 桌面应用程序

文章目录 前言1、开发环境准备2、Hello World !3、发布总结 前言 操作系统&#xff1a;Windows 10 企业版 LTSC 1809 IDE&#xff1a;Microsoft Visual Studio 2022 Community 说明&#xff1a;Windows 10 企业版 LTSC 1809 不支持 .NET 4.8.1 详情请查看官方说明文档 1、开发…

【华为OD机试】手机App防沉迷系统【C卷|100分】

【华为OD机试】-真题 !!点这里!! 【华为OD机试】真题考点分类 !!点这里 !! 题目描述 智能手机方便了我们生活的同时,也侵占了我们不少的时间。 “手机App防沉迷系统”能够让我们每天合理地规划手机App使用时间,在正确的时间做正确的事。 它的大概原理是这样的: 在一天24小…

The Log-Structured Merge-Tree (LSM-Tree) 论文阅读笔记

原论文&#xff1a;The Log-Structured Merge-Tree (LSM-Tree) LSM-Tree的简介和关键技术要点 LSM-Tree&#xff08;Log-Structured Merge-Tree&#xff09;是一种为高吞吐量读写操作优化的数据结构&#xff0c;特别适用于写入密集型的应用场景。它由Patrick O’Neil等人开发…

基于streamlit快速部署机器学习项目(Public URL)

基于streamlit的AIGC项目前端展示 1.Streamlit 简介与入门1.1 安装 Streamlit1.2 开发Streamlit应用程序1.3 启动并运行1.3.1 本地运行1.3.2 部署 现在LLM技术发展迅速&#xff0c;很多人在学习的时候&#xff0c;都想展示效果&#xff0c;并且想部署在服务器上&#xff0c;但是…

SpringBoot的核心内容之自动装配

不面试不知道&#xff0c;现在面试的问题真的是五花八门的&#xff0c;最近就有读者说&#xff0c;在面试的过程中&#xff0c;面试官还提到了关于最初版本的 Spring 还有 SpringMVC 以及现在的 SpringBoot 中的一些相关问题&#xff0c;比如他提到了这个 SpringBoot 的自动装配…

【人工智能基础】线性回归实验分析

实验使用到的库&#xff1a;numpy、matplotlib、scikit-learn 实验使用的开发环境&#xff1a;anaconda、jupyter 一、线性回归 线性回归就是使用一个线性函数&#xff08;多项式回归可以是曲线&#xff09;去拟合给定的训练集&#xff0c;测试时&#xff0c;对输入的x值&#…

Selenium一本通

Selenium中文官方文档 从一个测试脚本说起 from selenium import webdriver #引入selenium webdriver模块 driver webdriver.Firefox() #初始化Firefox浏览器的webdriver对象&#xff0c;启动浏览器 driver.get("http://cn.bing.com/") #跳转到对应页面 assert Bi…

Jammy@Jetson Orin - Tensorflow Keras Get Started: Concept

JammyJetson Orin - Tensorflow & Keras Get Started: Concept 1. 源由2. 模型2.1 推理流程2.1.1 获取图像2.1.2 算法识别2.1.3 判断决策 2.2 理想情况2.2.1 多因素输入2.2.2 理想识别概率 2.3 学习过程2.3.1 标记训练集2.3.2 损失函数2.3.3 训练网络2.3.4 渐进方法 3. 总…

jvm(JVM快速入门、stack栈、堆、GC垃圾回收、Arthas)

文章目录 1. JVM快速入门1.1. 结构图1.2. 类加载器ClassLoader1.3. 执行引擎Execution Engine1.4. 本地接口Native Interface1.5. Native Method Stack1.6. PC寄存器(程序计数器)1.7. Method Area方法区 2. stack栈3. 堆3.1. 堆体系概述3.1.1. 新生区3.1.2. 老年代3.1.3. 永久代…

Python 打包:pyinstaller

目录 为什么选择它用法详解所有命令常规-h-v、 --version--distpath DIR--workpath Workpath-y、 --noconfirm--upx dir upx_dir Upx-a、 --ascii--clean--log-level LEVEL 生成内容-D、 --onedir-F、 --onefile--specpath DIR-n NAME&#xff0c;--NAME NAME-w 执行程序 为什么…

python基础知识点(蓝桥杯python科目个人复习计划66)

今日复习内容&#xff1a;算法双周赛 第一题&#xff1a;疯狂星期六 题目描述&#xff1a; 麦肯鸡是一家名声在外的汉堡店&#xff0c;他们最近推出了一份名为vivo50的套餐&#xff0c;只需要在门口大声喊出vivo50&#xff0c;就可以获得这个套餐。 现在&#xff0c;请你打…

【VUE】解决 Element UI 中 el-tab 切换时 ECharts 渲染宽度问题

解决 Element UI 中 el-tab 切换时 ECharts 渲染宽度问题 在使用 Element UI 的 el-tabs 组件时&#xff0c;我们常常会遇到一个问题&#xff1a;当 ECharts 图表被放在非当前激活的 Tab 内时&#xff0c;图表无法正确渲染其宽度和高度。由于在 Tab 未激活状态下&#xff0c;图…

[Java、Android面试]_21_Dalvik VM与JVM的区别

欢迎查看合集&#xff1a; Java、Android面试高频系列文章合集 文章目录 1. 结构2. 编译3. 运行环境4. Dalvik进程管理 Dalvik是android系统中的虚拟机&#xff0c;JVM是java虚拟机。 Dalvik VM可以支持将已转化为.dex文件格式的java应用程序的运行&#xff0c;.dex是专为Dalvi…

Windows程序设计 - 字符与字符串处理

目录 字符编码的问题 ANSI Vs Unicode 函数版本区分 C RunTime库 老生常谈的ASCII和UNICODE支持性 安全字符串函数 推介的字符和字符串处理的方式 ASCII与UNICODE互转 Reference 字符编码的问题 对于一些像我这样的初学者&#xff1a;很喜欢认为字符串的处理就是将字…