算法期末复习

假币问题


时间复杂度为O(logn)

#include <iostream>
using namespace std;

int weightSum(int *coins, int begin, int end);
int findBadCoinIterate(int *coins, int length, int &model);
int findBadCoinRecursion(int *coins, int begin, int end, int &model);
void findBadCoinTest();

int main() {
	findBadCoinTest();
	return 0;
}

int weightSum(int *coins, int begin, int end) {
	int sum = 0;
	for(int i = begin;i <= end;i++) {
		sum += coins[i];
	}
	
	return sum;
}

//递归实现
int findBadCoinRecursion(int *coins, int begin, int end, int &model) {
	int n = end - begin + 1;
	if(n == 1) {
		return begin;//因为只剩一个硬币了
	}
	else if(n == 2) {
		return coins[begin] != model ? begin : end;//两个之中只有一个是假币,注意model肯定是真币,因为在n >= 3的时候求过了
	}
	
	int midl = n / 3 - 1;//A堆的最后一个
	int midr = 2 * (n / 3) + n % 3;//B堆的第一个
	int sumA = weightSum(coins, begin, midl + begin);//称重
	int sumB = weightSum(coins, midr + begin, end);
	if(sumA == sumB) {
		model = coins[begin];//找到真币,假币在中间那堆
		return findBadCoinRecursion(coins, midl + begin + 1, midr + begin - 1, model);
	}
	else {
		model = coins[midl + begin + 1];//真币在中间那堆
		int num = midl + 1;//A,B堆的硬币个数,A,B的硬币个数相等,A堆的数量是num = midl + begin - begin + 1 = midl + 1
		if(sumA == num * model) {
			return findBadCoinRecursion(coins, midr + begin, end, model);//假币在B堆
		}
		else {
			return findBadCoinRecursion(coins, begin, midl + begin, model);//假币在A堆
		}
	}
}

//迭代实现
int findBadCoinIterate(int *coins, int length, int &model) {
	int begin = 0;
	int end = length - 1;
	while(begin <= end) {
		int n = end - begin + 1;
		if(n == 1) {
			return begin;
		}
		else if(n == 2) {
			return coins[begin] != model ? begin : end;
		}
		int midl = n / 3 - 1;
		int midr = 2 * (n / 3) + n % 3;
		int sumA = weightSum(coins, begin, midl + begin);
		int sumB = weightSum(coins, midr + begin, end);
		if(sumA == sumB) {
			model = coins[begin];//找到真币
			begin =  midl + begin + 1;//假币在中间那堆,缩小范围
			end = midr + begin - 1;
		}
		else {
			model = coins[midl + begin + 1];
			int nAB = midl + 1;
			if(sumA == nAB * model) {
				begin = midr + begin;//假币在B堆,缩小范围
			}
			else {
				end = midl + begin;//假币在A堆,缩小范围
			}
		}
	}
	
	return -1;
}

//测试
void findBadCoinTest() {
	int contin = 1;
	do {
		system("cls");
		int n = 0;
		cout << "请输入硬币数量n(n > 3):";
		cin >> n;
		if(n < 3) {
			cout << "你输入的硬币数量太少了!" << endl;
			continue;
		}
		
		int *coins = new int[n];
		cout << "请输入硬币的重量:";
		for(int i = 0;i < n;i++) {
			cin >> coins[i];
		}
		
		int model = -1;
		int index = -1;
		
		cout << "--------递归结果--------\n" << endl;
		model = -1;
		index = findBadCoinRecursion(coins, 0, n - 1, model);
		if(coins[index] > model) {
			cout << "第" << index + 1 << "个硬币是假的,比其他硬币重了!" << endl;
		}
		else if(coins[index] < model) {
			cout << "第" << index + 1 << "个硬币是假的,比其他硬币轻了!" << endl;
		}
		else {
			cout << "没有假币!" << endl;
		}
		
		cout << "\n--------迭代结果--------" << endl;
		model = -1;
		index = findBadCoinIterate(coins, n, model);
		if(coins[index] > model) {
			cout << "第" << index + 1 << "个硬币是假的,比其他硬币重了!" << endl;
		}
		else if(coins[index] < model) {
			cout << "第" << index + 1 << "个硬币是假的,比其他硬币轻了!" << endl;
		}
		else {
			cout << "没有假币!" << endl;
		}
		
		delete []coins;
		
		cout << "继续检测输入1,退出输入0:";
		cin >> contin;
	} while (contin == 1);
	cout << "欢迎再次检测!" << endl;
}
def weight_sum(coins, begin, end):
    return sum(coins[begin:end+1])

def find_bad_coin_recursion(coins, begin, end, model):
    n = end - begin + 1
    if n == 1:
        return begin
    elif n == 2:
        return begin if coins[begin] != model else end

    midl = n // 3 - 1
    midr = 2 * (n // 3) + n % 3
    sumA = weight_sum(coins, begin, midl + begin)
    sumB = weight_sum(coins, midr + begin, end)

    if sumA == sumB:
        model = coins[begin]
        return find_bad_coin_recursion(coins, midl + begin + 1, midr + begin - 1, model)
    else:
        model = coins[midl + begin + 1]
        num = midl + 1
        if sumA == num * model:
            return find_bad_coin_recursion(coins, midr + begin, end, model)
        else:
            return find_bad_coin_recursion(coins, begin, midl + begin, model)

def find_bad_coin_iterate(coins, model):
    begin, end = 0, len(coins) - 1

    while begin <= end:
        n = end - begin + 1
        if n == 1:
            return begin
        elif n == 2:
            return begin if coins[begin] != model else end

        midl = n // 3 - 1
        midr = 2 * (n // 3) + n % 3
        sumA = weight_sum(coins, begin, midl + begin)
        sumB = weight_sum(coins, midr + begin, end)

        if sumA == sumB:
            model = coins[begin]
            begin, end = midl + begin + 1, midr + begin - 1
        else:
            model = coins[midl + begin + 1]
            nAB = midl + 1
            if sumA == nAB * model:
                begin = midr + begin
            else:
                end = midl + begin

    return -1

def find_bad_coin_test():
    contin = 1
    while contin == 1:
        n = int(input("Enter the number of coins (n > 3): "))
        if n < 3:
            print("You entered too few coins!")
            continue

        coins = [int(input("Enter weight of coin {}: ".format(i + 1))) for i in range(n)]

        model = -1

        print("--------Recursive Result--------\n")
        model = -1
        index = find_bad_coin_recursion(coins, 0, n - 1, model)
        if coins[index] > model:
            print("Coin {} is fake and heavier than the others!".format(index + 1))
        elif coins[index] < model:
            print("Coin {} is fake and lighter than the others!".format(index + 1))
        else:
            print("No fake coin!")

        print("\n--------Iterative Result--------")
        model = -1
        index = find_bad_coin_iterate(coins, model)
        if coins[index] > model:
            print("Coin {} is fake and heavier than the others!".format(index + 1))
        elif coins[index] < model:
            print("Coin {} is fake and lighter than the others!".format(index + 1))
        else:
            print("No fake coin!")

        contin = int(input("Continue testing? Enter 1 for yes, 0 for no: "))

    print("Welcome to test again!")

if __name__ == "__main__":
    find_bad_coin_test()

2第K大数

AB数组,升降序,元素不同,找出2n个数中第n大的数
O( min ( log M, log N ))

def func2(num1,num2,k):
    if not num1:
        return num2[k-1]
    if not num2:
        return num1[k-1]
    if k == 1:
        return min(num1[0], num2[0])
    k1 = k // 2
    k2 = k - k1
    if num1[k1-1] > num2[k2-1]:
        return func2(num1, num2[k2:], k-k2)
    else:
        return func2(num1[k1:], num2, k-k1)

print(func2([4,5,6],[1,2,3],4))

def func2(num1, num2, k):
    if not num1:
        return num2[-k]
    if not num2:
        return num1[-k]
    if k == 1:
        return max(num1[-1], num2[-1])
    k1 = k // 2
    k2 = k - k1
    if num1[-k1] < num2[-k2]:
        return func2(num1, num2[:-k2], k - k2)
    else:
        return func2(num1[:-k1], num2, k - k1)

print(func2([6,5,4], [3,2,1], 4))

3概率算法

求定积分、sin、cos、多项式、P236T7-3

定积分

时间复杂度O(n)

import random

def f(x):
    return x * x

def Darts(n, a, b, d):
    k = 0
    for i in range(n):
        x = (b - a) * random.random() + a  # Generate a random number in [a, b)
        y = d * random.random()

        if y <= f(x):
            k += 1

    return d * (b - a) * k / n

# Example usage:
n = 1000  # Replace with your actual value for n
a = 1.0   # Replace with your actual value for a
b = 2.0   # Replace with your actual value for b
d = 3.0   # Replace with your actual value for d

result = Darts(n, a, b, d)
print("Result:", result)

double Darts(int n,double a,double b,double d)  
{  
    static RandomNumber dart;  
    int k = 0;  
    for(int i=0; i<n; i++)  
    {  
        double x = (b-a)*dart.fRandom() + a;//产生[a,b)之间的随机数   
        double y = d * dart.fRandom();  
  
        if(y<=f(x))  
        {  
            k++;  
        }  
    }  
    return d*(b-a)*k/n;  
}  
  
double f(double x)  
{  
    return x*x;  
}  

洗牌

在n个不同的数中随机取出不重复的m个数
三种洗牌算法shuffle-CSDN博客
时间复杂度O(n)

void Knuth_Durstenfeld_Shuffle(vector<int>& arr)
{
    for (int i = arr.size() - 1; i >= 0; --i)
        {
            srand((unsigned)time(NULL));  // 每次循环都重新设置随机数种子
            swap(arr[rand() % (i + 1)], arr[i]);  // 交换当前元素与随机选择的元素
        }
}

答案时间复杂度可以表示为 O(n)

import random

def floyd(n, m, s):
    rnd = random.Random()
    y = 0
    while y < m:
        for j in range(n - m + 1, n + 1):
            u = rnd.random()
            k = 1 + int(j * u)
            if s[k] == 0:
                s[k] += 1
                y += 1
            elif s[j] == 0:
                s[j] += 1
                y += 1

void Floyd(int n, int m, int s[]){
    RandomNumber rnd;
    int y = 0;
    while(y < m){
        for(int j=n-m+1;j <=n;j++){
            double u=rnd.fRandom();
            int k =1+j*u;
            if(s[k] == 0) {
                s[k ]++;
                y++;}
            else if(s[j]==0){
                s[j]++;
                y++;
            }
        }
    }
}

4时间复杂度

三种方法求递归算法的时间复杂度(递推,master定理,递归树)_递归时间复杂度-CSDN博客

线性递推

特征方程和递归算法
【数列】特征方程与特征根
数列特征根相等怎么办? - 知乎用户的回答 - 知乎 https://www.zhihu.com/question/518854540/answer/2555589396

迭代法

等比数列: Sn=a1 (1-q^n)/ (1-q)
等差数列: Sn=(a1+an)*n/2
1869041321.jpg

主定理

1
image.png
2
image.png
image.png

5

2-7多项式乘积

-1232093038.jpg


2-9空间合并算法

image.png

def merge(a, left, mid, right):
    i = left
    j = mid + 1

    # 如果左半部分的最大值小于右半部分的最小值,说明已经有序,无需合并
    if a[mid] <= a[j]:
        return

    while i <= mid and j <= right:
        if a[i] <= a[j]:
            i += 1
        else:
            value = a[j]
            index = j

            # 将右半部分的元素插入到左半部分的适当位置
            while index > i:
                a[index] = a[index - 1]
                index -= 1

            a[i] = value
            i += 1
            mid += 1
            j += 1

# 示例
arr = [1, 2, 4, 5, 7, 8, 10, 3, 6, 9]
left = 0
mid = 6
right = len(arr) - 1

# 合并左右两部分
merge(arr, left, mid, right)

print("排序后的数组:", arr)

6

3-1 最长单调递增子序列。

设计一个O(n^2)时间的算法,找出由n个数组成的序列的最长单调递增子序列。

def longest_increasing_subsequence(arr):
    n = len(arr)
    if n == 0:
        return 0

    # 初始化dp数组,每个元素至少可以形成长度为1的递增子序列
    dp = [1] * n

    # 从第二个元素开始遍历
    for i in range(1, n):
        # 对于每个元素,从第一个元素到当前元素,检查是否能形成递增子序列
        for j in range(i):
            if arr[i] > arr[j]:
                # 如果可以形成递增子序列,更新dp数组
                dp[i] = max(dp[i], dp[j] + 1)

    # 返回dp数组中的最大值,即最长递增子序列的长度
    return max(dp)

# 示例
sequence = [10, 22, 9, 33, 21, 50, 41, 60, 80]
result = longest_increasing_subsequence(sequence)
print("最长递增子序列的长度为:", result)

3-4 二维背包问题

给定n种物品和一背包。物品i的重量是w,,体积是b;,其价值为v,背包的容量为c,容积为d。问应如何选择装入背包中的物品,使得装入背包中物品的总价值最大?在选择装入背包的物品时,对每种物品i只有两种选择,即装入背包或不装入背包。不能将物品i装入背包多次,也不能只装入部分的物品 i。试设计一个解此问题的动态规划算法,并分析算法的计算复杂性。
image.png

def multi_dimensional_knapsack(weights, volumes, values, capacity_w, capacity_b):
    n = len(weights)
    
    # 创建一个三维数组来存储最优解
    dp = [[[0] * (capacity_b + 1) for _ in range(capacity_w + 1)] for _ in range(n + 1)]
    
    for i in range(1, n + 1):
        for w in range(capacity_w + 1):
            for b in range(capacity_b + 1):
                # 如果当前物品重量和体积不超过背包容量
                if weights[i-1] <= w and volumes[i-1] <= b:
                    # 考虑将当前物品放入背包,更新最优解
                    dp[i][w][b] = max(dp[i-1][w][b], dp[i-1][w-weights[i-1]][b-volumes[i-1]] + values[i-1])
                else:
                    # 如果当前物品不能放入背包,继承上一行的最优解
                    dp[i][w][b] = dp[i-1][w][b]
    
    return dp[n][capacity_w][capacity_b]

# 示例
weights = [2, 1, 3]
volumes = [1, 2, 3]
values = [8, 7, 6]
capacity_w = 3
capacity_b = 3

result = multi_dimensional_knapsack(weights, volumes, values, capacity_w, capacity_b)
print("最大价值:", result)

7

Fibonacci序列的哈夫曼编码。

字符a~h出现的频率恰好是前8个 Fibonacci 数,它们的哈夫曼编码是什么?将结果推广到n个字符的频率恰好是前n个 Fibonacci 数的情形。

class Node:
    def __init__(self, char=None, freq=0):
        self.char = char
        self.freq = freq
        self.left = None
        self.right = None

def fibonacci_numbers(n):
    fib_sequence = [0, 1]
    while len(fib_sequence) < n:
        fib_sequence.append(fib_sequence[-1] + fib_sequence[-2])
    return fib_sequence

def build_huffman_tree(characters, frequencies):
    nodes = [Node(char=char, freq=freq) for char, freq in zip(characters, frequencies)]

    while len(nodes) > 1:
        # 排序节点列表,根据频率从小到大
        nodes = sorted(nodes, key=lambda x: x.freq)

        # 取出两个最小频率的节点
        left = nodes.pop(0)
        right = nodes.pop(0)

        # 创建新节点,作为左右子节点的父节点,频率为左右子节点频率之和
        parent = Node(freq=left.freq + right.freq)
        parent.left = left
        parent.right = right

        # 将新节点加入节点列表
        nodes.append(parent)

    # 返回哈夫曼树的根节点
    return nodes[0]

def generate_huffman_codes(node, current_code="", code_dict=None):
    if code_dict is None:
        code_dict = {}

    if node.char:
        code_dict[node.char] = current_code
    if node.left:
        generate_huffman_codes(node.left, current_code + "0", code_dict)
    if node.right:
        generate_huffman_codes(node.right, current_code + "1", code_dict)

    return code_dict

def main():
    characters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
    frequencies = fibonacci_numbers(8)

    huffman_tree = build_huffman_tree(characters, frequencies)
    huffman_codes = generate_huffman_codes(huffman_tree)

    for char, code in sorted(huffman_codes.items()):
        print(f"Character: {char}, Huffman Code: {code}")

if __name__ == "__main__":
    main()

8

5-1子集和问题。

问题描述:子集和问题的一个实例为<S, >。其中,S={X1,X2,…,Xn}是一个正整数的集合,c是一个正整数。子集和问题判定是否存在S的一个子集S1,使得∑x=c。试设计一个解子集和问题的回溯法。\

def backtrack(t):
    global n, c, r, cw, bestw, x, bestx, w

    if t > n:
        for i in range(1, n + 1):
            bestx[i] = x[i]
        if bestw == c:
            return True
        else:
            return False

    r -= w[t]

    if cw + w[t] <= c:
        x[t] = 1
        cw += w[t]
        if backtrack(t + 1):
            return True
        cw -= w[t]

    if cw + r >= bestw:
        x[t] = 0
        if backtrack(t + 1):
            return True

    r += w[t]
    return False

# Example usage:
n = 5  # Replace with your actual values
c = 10  # Replace with your actual values
r = sum(w)  # Assuming w is defined elsewhere in your code
cw = 0
bestw = float('-inf')
x = [0] * (n + 1)
bestx = [0] * (n + 1)
w = [2, 3, 4, 5, 6]  # Replace with your actual values

if backtrack(1):
    print("Solution found:")
    print("Best weight:", bestw)
    print("Selected items:", bestx[1:])
else:
    print("No solution found.")

bool backtrack(int t) {
    if (t > n) {
        for (int i = 1; i <= n; i++)
            bestx[i] = x[i];
        if (bestw == c)
            return true;
        else
            return false;
    }

    r -= w[t];

    if (cw + w[t] <= c) {
        x[t] = 1;
        cw += w[t];
        if (backtrack(t + 1))
            return true;
        cw -= w[t];
    }

    if (cw + r >= bestw) {
        x[t] = 0;
        if (backtrack(t + 1))
            return true;
    }

    r += w[t];
    return false;
}

5-3最小重量机器设计问题。

问题描述:设某一机器由n个部件组成,每种部件都可以从m个不同的供应商处购得。设wij是从供应商j处购得的部件i的重量,cij是相应的价格。试设计一个算法,给出总价格不超过c的最小重量机器设计。

# 设 w[i][j] 是从供应商j处购得的部件 i 的重量, c[i][j] 是相应的价格。
# 限定:价格不超过p 重量尽可能小

n = 3 # 零件数量
m = 2 # 供应商数量
p = 60 # 不应超过的价格

w = [[12, 15], [23, 24], [78, 70]] # 供应商j处购得的部件 i 的重量
c = [[8, 6], [13, 9], [14, 25]] # 相应的价格

min_weight = 10000
bast_choose = [None]*3

cur_choose = [None]*10
cur_weight = 0
cur_price = 0

def backtrack(t):
# 此处的t代表每一次遍历i供应商的零件
    global cur_weight, cur_price, cur_choose, min_weight, p, w, c
    if t == n:
        # 遍历到叶子结点
        if cur_weight < min_weight:
            min_weight = cur_weight

            for j in range(n):
                bast_choose[j] = cur_choose[j] + 1
            return
    else:
        for i in range(m): # 遍历供应商
            cur_choose[t] = i
            cur_weight += w[t][i]
            cur_price += c[t][i]

            if cur_weight < min_weight and cur_price <= p:
                # 该供应商的重量小于局部最优解 同时价格满足要求 则遍历其子树
                backtrack(t+1)

            cur_weight -= w[t][i]
            cur_price -= c[t][i]
            cur_choose[t] = 0

def main():
    backtrack(0)
    print('最小的重量是:%d'%min_weight)
    print('最佳选择是:',bast_choose)

if __name__ == '__main__':
    main()

9时间复杂度

查找

常见的查找算法有很多种,它们的时间复杂度不同。以下是一些常见的查找算法及其时间复杂度的比较:

顺序查找 (Sequential Search):

  • 时间复杂度:O(n)
  • 算法简单,适用于小规模数据。

二分查找 (Binary Search):

  • 仅适用于有序数组。
  • 时间复杂度:O(log n)
  • 比顺序查找效率高,但要求数据有序。

哈希查找 (Hash Search):

  • 平均情况下,时间复杂度为O(1)。但在最坏情况下可能为O(n)。
  • 需要合适的哈希函数,并处理哈希冲突。

树结构查找(二叉搜索树、平衡二叉搜索树、B树、B+树):

  • 二叉搜索树 (Binary Search Tree):
    • 平均时间复杂度:O(log n)
    • 可能出现最差情况O(n),例如树不平衡。
  • 平衡二叉搜索树 (AVL Tree):
    • 时间复杂度:O(log n)
    • 保持树的平衡,提高了查找效率。
  • B树和B+树:
    • 通常用于数据库索引,对磁盘存储友好,减少磁盘I/O。
    • 时间复杂度:O(log n)

跳表 (Skip List):

  • 在有序链表的基础上加入多级索引,提高查询效率。
  • 平均时间复杂度:O(log n)

这些算法的选择取决于具体的应用场景和数据特征。例如,对于有序数据,二分查找是一个不错的选择;对于哈希查找,需要考虑哈希函数的设计和处理冲突的方法。

排序

I7TLWWXADDXTVYF_L)~D4VJ.jpg

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值