📅  最后修改于: 2023-12-03 15:21:51.596000             🧑  作者: Mango
在 C 编程语言中,查找列表中的某个项是否存在是一个很常见的任务。如果列表是无序的,我们需要遍历整个列表来查找目标项。这种方法的时间复杂度是 O(n),其中 n 是列表的长度。
然而,如果列表是有序的,我们就可以采用更高效的算法来查找目标项。这种方法的时间复杂度是 O(log n)。
以下是一些常见的有序列表查找算法:
二分查找也被称为折半查找。它是一种非常基本的查找算法,能够快速地在有序列表中查找目标项。
算法的基本思想是:首先找到中间项,然后将目标项与中间项进行比较。如果它们相等,则找到了目标项;否则,如果目标项较小,则在前半部分继续查找;如果目标项较大,则在后半部分继续查找。每一次比较都将列表的长度减半。
下面是一个使用二分查找来查找整数 x 是否出现在有序列表 a 中的示例代码:
int binary_search(int a[], int n, int x) {
int left = 0, right = n - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (a[mid] == x) {
return mid;
} else if (a[mid] < x) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
时间复杂度的计算方式是基于这样一个事实:在每次循环中,列表的长度都会减半。因此,最坏情况下需要比较的次数就是 log2(n),其中 n 是列表的长度。
插值查找是对二分查找的改进。它利用了列表中元素的分布情况,通过插值来估计目标项的可能位置,并从该位置开始进行查找。这种方法对于元素在列表中均匀分布的情况效果特别好。时间复杂度仍然是 O(log n)。
以下是一个使用插值查找来查找整数 x 是否出现在有序列表 a 中的示例代码:
int interpolation_search(int a[], int n, int x) {
int left = 0, right = n - 1;
while (left <= right && x >= a[left] && x <= a[right]) {
int pos = left + (right - left) * (x - a[left]) / (a[right] - a[left]);
if (a[pos] == x) {
return pos;
} else if (a[pos] < x) {
left = pos + 1;
} else {
right = pos - 1;
}
}
return -1;
}
二叉查找树是一种字典树,可以用来实现有序列表。它的基本思想是:将列表中的元素插入到一个二叉树中,并保证它们有序。然后,通过比较目标项与当前节点的值,就可以逐步缩小查找范围。
这种方法的时间复杂度可以是 O(n),最差情况下会退化为一个链表,但通常情况下仍然是 O(log n)。
以下是一个使用二叉查找树来查找整数 x 是否出现在有序列表 a 中的示例代码:
typedef struct node {
int data;
struct node* left;
struct node* right;
} Node;
Node* insert(Node* root, int data) {
if (root == NULL) {
root = (Node*)malloc(sizeof(Node));
root->data = data;
root->left = root->right = NULL;
} else if (data < root->data) {
root->left = insert(root->left, data);
} else if (data > root->data) {
root->right = insert(root->right, data);
}
return root;
}
int search(Node* root, int x) {
if (root == NULL || root->data == x) {
return root == NULL ? -1 : 0;
} else if (x < root->data) {
return search(root->left, x);
} else {
return search(root->right, x);
}
}