当前位置: 首页 > article >正文

C/C++ 查找算法

一.二分算法

调整:

如果arr[mid] < x, min = mid + 1;

如果arr[mid] > x, max = mid - 1;

如果arr[mid] == x, 找到结果

终止条件:min >= max

二分查找--泛型情况

找第一个1:

找最后一个1:

注意:mid = (min + max + 1) /2

代码演示:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>

using namespace std;


void output(int* arr, int n, int ind = -1) {
	int len = 0;
	for (int i = 0; i < n; i++) {
		len += printf("%4d", i);
	}
	printf("\n");
	for (int i = 0; i < len; i++) {
		printf("-");
	}
	printf("\n");
	for (int i = 0; i < n; i++) {
		if (i == ind) printf("\033[1;32m");
		printf("%4d", arr[i]);
		if (i == ind) printf("\033[0m");
	}
	printf("\n");
	return;
}

int binary_search(int* arr, int n, int x) {
	int head = 0, tail = n - 1, mid;
	while (head <= tail) {
		mid = (head + tail) / 2;
		if (arr[mid] == x) return mid;
		if (arr[mid] < x) head = mid + 1;
		else tail = mid - 1;
	}
	return -1;
}

void test_binary_search(int n) {
	int* arr = (int*)malloc(sizeof(int) * n);
	arr[0] = rand() % 10;
	for (int i = 1; i < n; i++) arr[i] = arr[i - 1] + rand() % 10;
	output(arr, n);
	int x;
	while (~scanf("%d", &x)) {
		if (x == -1) break;
		int ind = binary_search(arr, n, x);
		output(arr, n, ind);
	}
}

#define EXP 1e-4

double f(double x) {
	if (x >= 0) x -= min(x, 3000.0) * 0.03;
	if (x >= 3000) x -= (min(x, 12000.0) - 3000) * 0.1;
	if (x >= 12000) x -= (min(x, 25000.0) - 12000) * 0.2;
	if (x >= 25000) x -= (min(x, 35000.0) - 25000) * 0.25;
	if (x >= 35000) x -= (min(x, 55000.0) - 35000) * 0.3;
	if (x >= 55000) x -= (min(x, 80000.0) - 55000) * 0.35;
	if (x >= 80000) x -= (x - 80000) * 0.45;
	return x;
}

double binary_algorithm(double y) {
	double head = 0, tail = 1000000, mid;
	while (tail - head >= EXP) {
		mid = (head + tail) / 2;
		if (f(mid) < y) head = mid;
		else tail = mid;
	}
	return head;
}

void test_binary_algorithm() {
	double y;
	while (~scanf("%lf", &y)) {
		if (y < 0) break;
		double x = binary_algorithm(y);
		printf("f(%.2lf) = %.2lf\n", x, y);
	}
}

int main() {
#define MAX_N 10
	test_binary_search(MAX_N);
	test_binary_algorithm();
	return 0;
}

二.跳跃表

对于原有链表中,对每个节点都设置了一个层高;

头节点的值为一个极小值,尾节点的值为一个极大值.

跳跃表本身,会维护节点中值的有序性

查找操作:

从左上角的节点开始查找:

若下一个节点的值比要查找的值大,则向下走一层;

若下一个节点的值比要查找的值小,则走向下一个节点;

插入操作:

跳跃表的每一个节点会随机一个层高

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#include<inttypes.h>

typedef struct Node {
	int key, level;
	struct Node* next, * down, * up;
}Node;

#define min(a,b) ((a) < (b) ? (a) : (b))
#define MAX_LEVEL 32
typedef struct Skiplist {
	Node* head, * tail;
	int max_level;
}Skiplist;

Node* getNewNode(int key, int n) {
	Node* nodes = (Node*)malloc(sizeof(Node) * n);
	for (int i = 0; i < n; i++) {
		nodes[i].key = key;
		nodes[i].level = i;
		nodes[i].next = NULL;
		nodes[i].down = ((i != 0)? nodes + i - 1 : NULL);
		nodes[i].up = ((i + 1 < n)? nodes + i + 1 : NULL);
	}
	return nodes + n - 1;
}

Skiplist* getNewSkiplist(int n) {
	Skiplist* s = (Skiplist*)malloc(sizeof(Skiplist));
	s->head = getNewNode(INT32_MIN, n);
	s->tail = getNewNode(INT32_MAX, n);
	s->max_level = n;
	Node* p = s->head, *q = s->tail;
	while (p) {
		p->next = q;
		p = p->down;
		q = q->down;
	}
	while (s->head->level != 0) s->head = s->head->down;
	return s;
}


Node* find(Skiplist* s, int x) {
	Node* p = s->head;
	while (p && p->key != x) {
		if (p->next->key <= x) p = p->next;
		else p = p->down;
	}
	return p;
}

#define MAX_RAND_N 10000

double randDouble() {
	int n = (rand() % MAX_RAND_N);
	return (n * 1.0 / (double)MAX_RAND_N);
}

int randLevel(Skiplist* s) {
	int level = 1;
	double p = 1.0 / 2.0;
	double temp = randDouble();
	while (temp < p) {
		level += 1;
		temp = randDouble();
	}
	return min(s->max_level, level);
}

void insert(Skiplist* s, int x) {
	int level = randLevel(s);
	while (s->head->level + 1 < level) {
		s->head = s->head->up;
	}
	Node* node = getNewNode(x, level);
	Node* p = s->head;
	printf("insert begin\n");
	fflush(stdout);
	while(p->level != node->level) p = p->down;
	while (p) {
		while (p->next->key < node->key) p = p->next;
		node->next = p->next;
		p->next = node;
		p = p->down;
		node = node->down;
	}
	return;
	
}

void clearNode(Node* p) {
	if (p == NULL) return;
	free(p);
	return;
}

void clearSkiplist(Skiplist* s) {
	Node* p = s->head, *q;
	while (p->level != 0) p = p->down;
	while (p) {
		q = p->next;
		clearNode(p);
		p = q;
	}
	free(s);
}

void output(Skiplist* s) {
	Node* p = s->head;
	int len = 0;
	for (int i = 0; i < s->head->level; i++) {
		len += printf("%4d", i);
	}
	printf("\n");
	for (int i = 0; i < len; i++) printf("-");
	printf("\n");
	while (p->level > 0) p = p->down;
	while (p) {
		bool flag = (p->key != INT32_MIN && p->key != INT32_MAX);
		for (Node* q = p; q && flag; q = q->up) {
			printf("%4d", q->key);
		}
		if(flag) printf("\n");
		p = p->next;
	}
}

int main() {
	srand(time(0));
	int x;
	Skiplist* s = getNewSkiplist(MAX_LEVEL);
	while (~scanf("%d", &x)) {
		if (x == -1)break;
		insert(s, x);
	}
	output(s);
	while (~scanf("%d", &x)) {
		Node* p = find(s, x);
		if (p) {
			printf("key: %d, level: %d", p->key, p->level);
		}else {
			printf("NULL\n");
		}
	}
	clearSkiplist(s);
	return 0;
}

三.哈希表与布隆过滤器

哈希表:

哈希冲突:不同元素经过哈希函数被映射到了同一个位置.

哈希冲突的解决办法:

1.开放定址法

如果发生冲突,将新的元素按照一定规则,去重新计算得出一个新的下标;

2.再哈希法

如果发生冲突,用其他的哈希函数进行计算得出新的下标;

3.建立公共溢出区法

  用另一种数据结构来存储多余的元素;

4.链式地址法(推荐)

每个下标中维护一个链表;

布隆过滤器:

传统哈希表,储存空间和元素数量强相关;

布隆过滤器,储存空间和元素数量弱相关;

若经过多个哈希函数运算,出现的结果相同,则布隆过滤器就认为此元素出现过;

注意,并不是一定出现过,而是大概率出现过;

如何减少误判:增加布隆过滤器的底层存储空间,降低重复的概率;

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>

#include<iostream>

using namespace std;



typedef struct Node {
	char* s;
	struct Node* next;
}Node;

typedef struct HashTable {
	Node* data;
	int cnt, size;
}HashTable;

Node* getNewNode(const char* s) {
	Node* p = (Node*)malloc(sizeof(Node));
	p->s = _strdup(s);
	p->next = NULL;
	return p;
}

HashTable* getNewHashTable(int n) {
	HashTable* h = (HashTable*)malloc(sizeof(HashTable));
	h->data = (Node*)malloc(sizeof(Node) * n);
	for (int i = 0; i < n; i++) {
		h->data[i].next = NULL;
	}
	h->size = n;
	h->cnt = 0;
	return h;
}

int hash_func(const char* s) {
	int seed = 131, h = 0;
	for (int i = 0; s[i]; i++) {
		h = h * seed + s[i];
	}
	return h & 0x7fffffff;
}

bool find(HashTable* h, const char* s) {
	int hcode = hash_func(s), ind = hcode % h->size;
	Node* p = h->data[ind].next;
	while (p) {
		if (strcmp(p->s, s) == 0)return true;
		p = p->next;
	}
	return false;
}


void swapHashTable(HashTable* h1, HashTable* h2) {
	swap(h1->data, h2->data);
	swap(h1->cnt, h2->cnt);
	swap(h1->size, h2->size);
	return;
}

void expand(HashTable* h);

bool insert(HashTable* h, const char* s) {
	if (h->cnt >= h->size * 2) {
		expand(h);
	}
	int hcode = hash_func(s), ind = hcode% h->size;
	Node* p = getNewNode(s);
	p->next = h->data[ind].next;
	h->data[ind].next = p;
	h->cnt += 1;
	return true;
}



void clearNode(Node* p) {
	if (p == NULL) return;
	if (p->s) free(p->s);
	free(p);
	return;
}

void clearHashTable(HashTable* h) {
	if (h == NULL) return;
	for (int i = 0; i < h->size; i++) {
		Node* p = h->data[i].next, * q;
		while (p) {
			q = p->next;
			clearNode(p);
			p = q;
		}
	}
	free(h->data);
	free(h);
	return;
}


void  expand(HashTable* h) {
	HashTable* new_h = getNewHashTable(h->size * 2);
	for (int i = 0; i < h->size; i++) {
		Node* p = h->data[i].next;
		while (p) {
			insert(new_h, p->s);
			p = p->next;
		}
	}
	swapHashTable(h, new_h);
	clearHashTable(new_h);
}
void output(HashTable* h) {
	printf("\n\nHash Table(%d/%d): \n", h->cnt, h->size);
	for (int i = 0; i < h->size; i++) {
		printf("%d:", i);
		Node* p = h->data[i].next;
		while (p) {
			printf("%s -> ", p->s);
			p = p->next;
		}
		printf("\n");
	}
	return;
}

int main() {
	srand(time(0));
	char s[100];
#define MAX_N 2
	HashTable* h = getNewHashTable(MAX_N);
	while (~scanf("%s", s)) {
		if (strcmp(s, "end") == 0) break;
		insert(h, s);
	}
	output(h);
	while (~scanf("%s", s)) {
		printf("find(%s) = %d\n", s, find(h, s));
	}
	return 0;
}

代码练习

1. 两数之和 - 力扣(LeetCode)

哈希表做法

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> h;
        vector<int> ret(2);
        for(int i = 0; i < nums.size();i++) {
            if(h.find(target -nums[i]) != h.end()) {
                ret[0] = h[target - nums[i]];
                ret[1] = i;
                break;
            }
            h[nums[i]] = i;
        }
        return ret;
    }
};

二分查找做法

class Solution {
public:

    int binary_search(vector<int>&nums, vector<int>&ind, int b, int x) {
        int head = b, tail = nums.size() - 1, mid;
        while(head <= tail) {
            mid = (head + tail) /2;
            if(nums[ind[mid]] == x) return mid;
            if(nums[ind[mid]] < x) head = mid + 1;
            else tail = mid - 1;
        }
        return -1;
    }

    vector<int> twoSum(vector<int>& nums, int target) {
        int n = nums.size();
        vector<int> ind(n, 0);
        for(int i = 0; i < n; i++) ind[i] = i;
        sort(ind.begin(), ind.end(), [&](int i , int j) {
            return nums[i] < nums[j];
        });
        vector<int> ret(2);
        for(int i = 0; i < n; i++) {
            int j = binary_search(nums, ind, i + 1, target - nums[ind[i]]);
            if(j == -1) continue;
            ret[0] = ind[j];
            ret[1] = ind[i];
        }
        //if(ret[0] >ret[1]) swap(ret[0], ret[1]);
        return ret;
    }
};

35. 搜索插入位置 - 力扣(LeetCode)

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int head = 0, tail = nums.size(), mid;
        while(head < tail) {
            mid = (head + tail) /2;
            if(nums[mid] < target) head = mid + 1;
            else tail = mid;
        }
        return head;
    }
};

217. 存在重复元素 - 力扣(LeetCode)

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        unordered_set<int> h;
        for(auto x: nums) {
            if(h.find(x) != h.end()) return true;
            h.insert(x);
        }
        return false;
    }
};

349. 两个数组的交集 - 力扣(LeetCode)

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> h;
        vector<int> ret;
        for(auto x: nums1) h.insert(x);
        for(auto x: nums2) {
            if(h.find(x) == h.end()) continue;
            ret.push_back(x);
            h.erase(h.find(x));
        }
        return ret;
    }
};

3. 无重复字符的最长子串 - 力扣(LeetCode)

class Solution {
public:
    bool check(string &s, int l) {
        int cnt[256] = {0}, k = 0;
        for(int i = 0; s[i]; i++) {
            cnt[s[i]] += 1;
            if(cnt[s[i]] == 1) k += 1;
            if(i >= l) {
                cnt[s[i - l]] -= 1;
                if(cnt[s[i - l]] == 0) k -= 1;
            }
            if (l == k) return true;
        }
        return false;;
    }

    int lengthOfLongestSubstring(string s) {
         int head = 1, tail = s.size(), mid;
         //111100000
         while(head < tail) {
            mid = (head +tail + 1) /2;
            if(check(s,mid)) head = mid;
            else tail = mid - 1;
         } 
         return head;
    }
};

4. 寻找两个正序数组的中位数 - 力扣(LeetCode)

class Solution {
public:
    int findK(vector<int> &n1, int ind1, vector<int> &n2, int ind2, int k) {
        int n = n1.size(), m = n2.size();
        if(k == 1) {
            int a = (n1.size() == ind1 ? INT32_MAX: n1[ind1]);
            int b = (n2.size() == ind2 ? INT32_MAX: n2[ind2]);
            return min(a,b);
        }
        if(n == ind1) return n2[k - 1];
        if(m == ind2) return n1[k - 1];
        int cnt1 = min(k / 2 , n - ind1);
        int cnt2 = min(k - cnt1, m - ind2);
        cnt1 = k - cnt2;
        if(n1[ind1 + cnt1 - 1] <= n2[ind2 + cnt2 - 1]) {
            return findK(n1,ind1 + cnt1, n2, ind2, k - cnt1);
        }
        return findK(n1, ind1, n2 ,ind2 + cnt2, k - cnt2);
    }

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size(), m = nums2.size();
        if((n + m) % 2 == 1) return findK(nums1, 0, nums2, 0, (n + m) / 2 + 1);
        double a = findK(nums1, 0, nums2, 0, (n + m) / 2);
        double b = findK(nums1, 0, nums2, 0, (n + m) / 2 + 1);
        return (a + b) /2.0;
    }
};


http://www.kler.cn/a/441867.html

相关文章:

  • 蓝桥杯小白备考指南
  • vue+高德API搭建前端Echarts图表页面
  • 网络安全 | 什么是正向代理和反向代理?
  • STL--set(集合)
  • 双向耦合粒子追踪稳态求解器找到未定义的值?
  • Linux 音视频入门到实战专栏(视频篇)视频编解码 MPP
  • 入探讨网络安全:技术与策略的双重防线深
  • 创建线程 ---- 实例
  • 每天40分玩转Django:Django缓存系统
  • 探索:为什么数组数据后端确要求前端传递,拼接的字符呢
  • 乳腺癌多模态诊断解释框架:CNN + 可解释 AI 可视化
  • 基于MNE的EEGNet 神经网络的脑电信号分类实战(附完整源码)
  • CAD xy坐标标注(跟随鼠标位置实时移动)——C#插件实现
  • dify智能体系列:selenium有啥好玩的?
  • 如何为IntelliJ IDEA配置JVM参数
  • springboot中Controller内文件上传到本地以及阿里云
  • 【Prompt Engineering】2.迭代优化
  • 【附源码】Electron Windows桌面壁纸开发中的 CommonJS 和 ES Module 引入问题以及 Webpack 如何处理这种兼容
  • 判题机的开发(代码沙箱、三种模式、工厂模式、策略模式优化、代理模式)
  • 单片机:实现蜂鸣器数码管的显示(附带源码)
  • Numpy基本介绍
  • Leetcode打卡:形成目标字符串需要的最少字符串数II
  • 如何在 Linux 服务器上部署 Pydio Cells 教程
  • STM32F407ZGT6-UCOSIII笔记7:优先级反转现象
  • 【图形渲染】【Unity Shader】【Nvidia CG】有用的参考资料链接
  • Composer指定php版本执行(windows)