插入排序,选择排序,二分归并排序,最大子数组和,哈夫曼编码

通过代码实现随机隐去个人身份证号码中的 4 位数字(号码信息在输入变量运行后可用*字符或其它字符表示掩去个人信息),并用 1~9 之间的随机数取代,将此序列称为 ID_Number,分别用插入排序法、选择排序法、二分归并排序法、快速排序算法实现对 ID_Number 的排序。(40 分)

要求:

(1)随机隐去和取代过程需用代码实现,替代后的结果需要显示;

(2)几种方法需要分别写出伪代码,选择自己熟悉的平台和语言编写并运行代码;

(3)关键行代码需要对算法进行注释;

随机隐藏四位数字,并用随机数取代

伪代码

FUNCTION hide(id_num)

    num_list = 将 id_num 转为 list 类型

    hide_id = 在 range(len(num_list)) 中生成 4 个不重复的随机数下标

    # 隐藏身份证号码

    FOR i IN hide_id

        num_list[i] = '*'

    hid_num = 将 num_list 转为字符串类型

    输出 隐藏四位数后的字符串 hid_num

    # 用1-9的随机数替代隐藏的四位数

    nums_list = []

    FOR num IN num_list

        IF num == '*'

            new_num = 随机生成 1-9 之间的随机数

            nums_list.append(new_num)

        ELSE

            nums_list.append(num)

    nums = 将 nums_list 转为字符串类型

    输出 替代隐藏四位数后的字符串 nums

    返回 nums_list 作为函数的输出

END_FUNCTION

1.1插入排序法

伪代码:

FUNCTION insertion_sort(nums_list)

    FOR i IN range(1, len(nums_list))

        # 暂存当前要插入的元素

        temp = nums_list[i]

        j = i - 1

        # 从已排序序列末尾开始向前遍历,寻找插入位置

        WHILE j >= 0 AND nums_list[j] > temp

            # 将已排序序列中比当前元素大的元素向后移动

            nums_list[j+1] = nums_list[j]

            j -= 1

        END WHILE

        # 找到插入位置,将暂存的元素插入到列表中

        nums_list[j+1] = temp

    END FOR

    RETURN nums_list

END_FUNCTION

1.2选择排序法

伪代码:

FUNCTION selection_sort(nums_list)

    # 对 nums_list 进行选择排序

    FOR i IN range(len(nums_list)-1)

        # 找到未排序部分中最小的元素

        min = i

        FOR j IN range(i+1, len(nums_list))

            IF nums_list[j] < nums_list[min]

                # 如果当前元素比已知最小元素还小,则更新最小元素索引

                min = j

            END IF

        END FOR

        # 将找到的最小元素与未排序部分的起始元素交换

        nums_list[i], nums_list[min] = nums_list[min], nums_list[i]

    END FOR

    RETURN nums_list

END_FUNCTION

1.3二分归并排序

伪代码:

FUNCTION merge_sort(nums_list)

    n = len(nums_list)

    IF n <= 1

        RETURN nums_list

    END IF

    mid = n // 2

    # 对左右两个部分进行递归排序

    left = merge_sort(nums_list[:mid])

    right = merge_sort(nums_list[mid:])

    # 将两个排好序的子序列合并成一个有序的整体

    result = []

    l, r = 0, 0 # 左右指针, 用来遍历 left 和 right 子序列

    WHILE l < len(left) AND r < len(right)

        IF left[l] < right[r] # 比较左右子序列的当前元素大小

            result.append(left[l]) # 将 left 子序列的当前元素添加到结果序列中

            l += 1 # 左指针向后移动,遍历下一个元素

        ELSE

            result.append(right[r])

            r += 1 # 右指针向后移动,遍历下一个元素

        END IF

    END WHILE

    result.extend(left[l:]) # 将 left 子序列中剩余元素添加到结果序列

    result.extend(right[r:]) # 将 right 子序列中剩余元素添加到结果序列

    RETURN result

END_FUNCTION

1.4快速排序

伪代码:

FUNCTION quick_sort(nums_list)

    IF len(nums_list) <= 1 # 如果序列长度小于等于1,直接返回该序列(递归停止条件)

        RETURN nums_list

    END IF

    pivot = nums_list[0]  # 以序列的第一个元素作为基准数

    left, right, mid = [], [], [pivot]  # 左、右、中间三个临时数组

    FOR i IN nums_list[1:]

        IF i < pivot

            left.append(i) #将比pivot小的元素放入left列表

        ELSE IF i > pivot

            right.append(i) #将比pivot大的元素放入right列表

        ELSE

            mid.append(i) #将相等的元素放入mid列表

        END IF

    END FOR

    # 对left和right分别进行递归排序,并将mid列表与left和right拼接成结果列表

    result = quick_sort(left) + mid + quick_sort(right)

    RETURN result

END_FUNCTION

import random
from datetime import datetime

def hide(id_num):
    # 随机隐藏四位身份证号码
    num_list = list(id_num) #创建一个空列表,将 id_num 字符串中的每一个字符都转为一个元素,并放入到 num_list 列表中
    hide_id = random.sample(range(len(num_list)), 4) #在 num_list 的下标范围内随机生成 4 个不重复的下标
    #遍历这四个下标将这四位数字用*代替
    for i in hide_id:
        num_list[i] = '*'
    hid_num = ''.join(num_list) #将处理后的数字连接起来,变成字符串
    print(f"随机隐藏四位身份证号码后为: {hid_num}")

    # 用1-9的随机数替代隐藏的四位号码
    nums_list = [str(random.randint(1, 9)) if num == "*" else num for num in num_list]
    nums = ''.join(nums_list)
    print(f"随机数替代四位隐藏号码后为: {nums}")
    return nums_list

# 使用插入排序对身份证号码进行排序
def insertion_sort(nums_list):
    for i in range(1, len(nums_list)):
        # 从第 2 个元素开始遍历整个列表
        temp = nums_list[i] ## 暂存当前要插入的元素
        j = i - 1
        # 扫描已排序序列中的元素,寻找插入位置的前一位
        while j >= 0 and nums_list[j] > temp:
            # 如果已排序序列比当前元素大,则依次将它后移
            nums_list[j+1] = nums_list[j]
            # 继续向前扫描已排序序列中的元素
            j -= 1
            # 找到了合适的插入位置,将当前元素插入该位置
            nums_list[j+1] = temp
    return nums_list

 # 使用选择排序对身份证号码进行排序   
def selection_sort(nums_list):
    for i in range(len(nums_list)-1):
        # 找到未排序序列中最小的元素
        min = i
        for j in range(i+1, len(nums_list)):
            if nums_list[j] < nums_list[min]:
                # 如果当前元素比已知最小元素还小,则更新最小元素下标
                min = j
        # 将找到的最小元素与未排序序列的第一个元素交换
        nums_list[i], nums_list[min] = nums_list[min],nums_list[i]
    return nums_list

def merge_sort(nums_list):
    n = len(nums_list)
    if n <= 1:
        return nums_list
    mid = n // 2
    # left,right 采用归并排序后形成的有序的新的列表
    left = merge_sort(nums_list[:mid])
    right = merge_sort(nums_list[mid:])
    # 将两个有序的子序列合并为一个新的整体
    # merge(left,right)
    result = []
    l, r = 0, 0  # 左右指针,用来遍历 left 和 right 子序列
    while l < len(left) and r < len(right):
        if left[l] < right[r]: # 比较左右子序列的当前元素大小
            result.append(left[l])# 将 left 子序列的当前元素添加到结果序列中
            l += 1 # 左指针向后移动,遍历下一个元素
        else:
            result.append(right[r])
            r += 1 # 右指针向后移动,遍历下一个元素
    result.extend(left[l:])  # 将 left 子序列中剩余元素添加到结果序列
    result.extend(right[r:])  # 将 right 子序列中剩余元素添加到结果序列
    return result

def quick_sort(nums_list):
    if len(nums_list) <= 1: # 如果序列长度小于等于1,直接返回该序列(递归停止条件)
        return nums_list
    pivot = nums_list[0]  # 以序列的第一个元素作为基准数
    left, right, mid = [], [], [pivot]  # 左、右、中间三个临时数组
    for i in nums_list[1:]:
        if i < pivot:
            left.append(i) #对left列表排序
        elif i > pivot:
            right.append(i) #对right列表排序
        else:
            mid.append(i)
    result = quick_sort(left) + mid + quick_sort(right)
    return result


id_number = "420114199605512516"  # 待处理的身份证号码
nums_list=hide(id_number)
insertion_sort(nums_list) #插入排序
print(f"使用插入排序对身份证号码进行排序后为: {''.join(insertion_sort(nums_list))}")
selection_sort(nums_list) #选择排序
print(f"使用选择排序对身份证号码进行排序后为: {''.join(selection_sort(nums_list))}")
merge_sort(nums_list) #二分归并排序
print(f"使用二分归并排序对身份证号码进行排序后为: {''.join(merge_sort(nums_list))}")
quick_sort(nums_list) #快速排序
print(f"使用快速排序归并排序对身份证号码进行排序后为: {''.join(quick_sort(nums_list))}")

二、题二

利用第一题中的序列 ID_Number,分别用分治法和动态规划法求出最大非空子数组(子数组和最大,且为连续序列,子序列限制长度为6位数),确定首末位置,输出为最大子数组序列,及子序列的和。(30 分)

要求:

(1)两种方法需要分别写出伪代码,选择自己熟悉的平台和语言编写并运行代码;

(2)关键行代码需要对算法进行注释;

(3)输出包括首末位置、最大子数组以及子数组的和;

2.1分治法

伪代码:

FUNCTION max_subarray(nums: [int], left: int, right: int) -> [int, int, int]:

    IF left == right

        # 如果只有一个元素,那么它即为最大的子数组

        RETURN left, right, nums[left]

    # 如果有多个元素,那么可以把数组分为左半部分、右半部分和中间部分三个子数组

    mid = (left + right) // 2

    # 求解左半部分、右半部分和跨过中间部分三个子数组的解

    left_start, left_end, left_sum = max_subarray(nums, left, mid)

    right_start, right_end, right_sum = max_subarray(nums, mid + 1, right)

    cross_start, cross_end, cross_sum = max_cross_subarray(nums, left, mid, right)

    IF left_sum >= right_sum AND left_sum >= cross_sum

        # 左半部分子数组的和最大

        RETURN left_start, left_end, left_sum

    ELIF right_sum >= left_sum AND right_sum >= cross_sum

        # 右半部分子数组的和最大

        RETURN right_start, right_end, right_sum

    ELSE

        # 跨越中间部分子数组的和最大

        RETURN cross_start, cross_end, cross_sum

FUNCTION max_cross_subarray(nums: [int], left: int, mid: int, right: int) -> [int, int, int]:

    # 求解包含 mid 的最大左子数组

    left_sum = float('-inf')

    sum = 0

    FOR i from mid DOWNTO left

        sum += nums[i]

        IF sum > left_sum

            left_sum = sum

            max_left = i

    # 求解包含 mid 的最大右子数组

    right_sum = float('-inf')

    sum = 0

    FOR j from mid + 1 TO right

        sum += nums[j]

        IF sum > right_sum

            right_sum = sum

            max_right = j       

    # 返回跨越中间部分的子数组

RETURN max_left, max_right, left_sum + right_sum

FUNCTION sliding_window(nums: [int]) -> [int, int, int]:

    max_sum = float('-inf')

    max_start, max_end = None, None

    window_start = 0

    window_sum = SUM(nums[:6])  # 窗口大小为 6,求出第一个窗口中元素的和

    # 滑动窗口从左往右滑动,计算最大子数组的和、起始位置和结束位置

    FOR window_end FROM 6 TO LENGTH(nums) - 1

        IF window_sum > max_sum

            # 更新最大子数组的和、起始位置和结束位置

            max_sum = window_sum

            max_start = window_start

            max_end = window_end - 1

        # 滑动窗口向右滑动,更新窗口元素的和以及窗口的起始位置

        window_sum = window_sum - nums[window_start] + nums[window_end]

        window_start = window_start + 1

RETURN max_start, max_end, max_sum

def max_subarray(nums, left, right):
    if left == right:
        # 如果只有一个元素,那么它即为最大的子数组
        return left, right, nums[left]
    
    # 如果有多个元素,那么可以把数组分为左半部分、右半部分和中间部分三个子数组
    mid = (left + right) // 2
    # 求解左半部分、右半部分和中间部分三个子数组的解
    # 注意此处需要分别加上 mid 和 mid + 1,因为 mid 既属于左半部分也属于右半部分,相邻两个元素才是子数组
    left_start, left_end, left_sum = max_subarray(nums, left, mid)
    right_start, right_end, right_sum = max_subarray(nums, mid + 1, right)
    cross_start, cross_end, cross_sum = max_cross_subarray(nums, left, mid, right)
    # 取左半部分、右半部分和中间部分三个子数组的解中的最大值
    if left_sum >= right_sum and left_sum >= cross_sum:
        return left_start, left_end, left_sum
    elif right_sum >= left_sum and right_sum >= cross_sum:
        return right_start, right_end, right_sum
    else:
        return cross_start, cross_end, cross_sum
    
def max_cross_subarray(nums, left, mid, right):
    # 求解中间子数组的最大和
    
    # 求解包含 mid 的最大左子数组
    left_sum = float('-inf')
    sum = 0
    for i in range(mid, left - 1, -1):
        sum += nums[i]
        if sum > left_sum:
            left_sum = sum
            max_left = i
    
    # 求解包含 mid + 1 的最大右子数组
    right_sum = float('-inf')
    sum = 0
    for j in range(mid + 1, right + 1):
        sum += nums[j]
        if sum > right_sum:
            right_sum = sum
            max_right = j
    
    return max_left, max_right, left_sum + right_sum
    
def sliding_window(nums):
    # 用滑动窗口求出最大的六位数的子数组
    max_sum = float('-inf')
    max_start = None
    max_end = None
    
    window_start = 0
    window_sum = sum(nums[:6])
    
    for window_end in range(6, len(nums)):
        if window_sum > max_sum:
            max_sum = window_sum
            max_start = window_start
            max_end = window_end - 1
        
        window_sum = window_sum - nums[window_start] + nums[window_end]
        window_start += 1
        
    return max_start, max_end, max_sum
    
def solve(num):
    id_number = "420119199605962536"
    num_list = list(id_number)
    nums = list(map(int, num_list))
    subarray_start, subarray_end, subarray_sum = sliding_window(nums)
    start, end, sum = max_subarray(nums, subarray_start, subarray_end)
    subseq = nums[start: end + 1]
    print(f"最大子串的首尾位置:{start}, {end}")
    print(f"最大子序列:{subseq}")
    print(f"最大子数组和: {sum}")

2.2动态规划

伪代码

FUNCTION max_subarray(nums: [int]) -> [int, int, int]:

    # 动态规划求解最大子数组

    n = LENGTH(nums)

    # 用 dp[i] 存储以 nums[i] 结尾的最大子数组的和

    dp = ARRAY(0, n)

    dp[0] = nums[0]

    start, end = 0, 0

    max_sum = nums[0]

    # 动态规划求解以 nums[i] 结尾的最大子数组的和

    FOR i FROM 1 TO n - 1

        IF dp[i - 1] > 0

            dp[i] = dp[i - 1] + nums[i]

        ELSE

            dp[i] = nums[i]

            # 如果 dp[i] 小于等于 0,则以 nums[i] 结尾的子数组不是最大子数组的一部分,需要重新搜索

            IF dp[i] <= 0

                start = i   

        # 记录最大子数组的和及其结束位置

        IF dp[i] > max_sum

            max_sum = dp[i]

            end = i

RETURN start, end, max_sum

FUNCTION sliding_window(nums: [int]) -> [int, int, int]:

    max_sum = float('-inf')

    max_start, max_end = None, None

    window_start = 0

    window_sum = SUM(nums[:6])  # 窗口大小为 6,求出第一个窗口中元素的和

    # 滑动窗口从左往右滑动,计算最大子数组的和、起始位置和结束位置

    FOR window_end FROM 6 TO LENGTH(nums) - 1

        IF window_sum > max_sum

            # 更新最大子数组的和、起始位置和结束位置

            max_sum = window_sum

            max_start = window_start

            max_end = window_end - 1

        # 滑动窗口向右滑动,更新窗口元素的和以及窗口的起始位置

        window_sum = window_sum - nums[window_start] + nums[window_end]

        window_start = window_start + 1

RETURN max_start, max_end, max_sum

def max_subarray(nums):
    # 动态规划求解最大子数组
    n = len(nums)
    # 用 dp[i] 存储以 nums[i] 结尾的最大子数组的和
    dp = [0] * n
    dp[0] = nums[0]
    start = end = 0
    max_sum = nums[0]
    
    for i in range(1, n):
        # 判断以 nums[i] 结尾的子数组是否包含 nums[i],如果大于 nums[i],则包含
        if dp[i - 1] > 0:
            dp[i] = dp[i - 1] + nums[i]
        else:
            dp[i] = nums[i]
            # 如果 dp[i] 小于等于 0,则以 nums[i] 结尾的子数组不是最大子数组的一部分,需要重新搜索
            if dp[i] <= 0:
                start = i
        if dp[i] > max_sum:
            max_sum = dp[i]
            end = i
    return start, end, max_sum

def sliding_window(nums):
    max_sum = float('-inf')
    max_start,max_end = None,None
    
    window_start = 0
    window_sum = sum(nums[:6])
    
    for window_end in range(6, len(nums)):
        if window_sum > max_sum:
            max_sum ,max_start,max_end= window_sum,window_start,window_end - 1
        window_sum = window_sum - nums[window_start] + nums[window_end]
        window_start += 1
        
    return max_start, max_end, max_sum

id_number = "420119199605962536"
num_list = list(id_number)
nums = list(map(int, num_list))
subarray_start, subarray_end, subarray_sum = sliding_window(nums)
start, end, sum = max_subarray(nums[subarray_start: subarray_end + 1])
start += subarray_start
end += subarray_start
subseq = nums[start: end + 1]
print(f"最大子串的首尾位置:{start}, {end}")
print(f"最大子序列:{subseq}")
print(f"最大子数组和: {sum}")

三、题三

霍夫曼编码(Huffman Coding)实现。(30 分)

要求:

(1)将个人名字的大写英文字符作为字符数组,以“周杰伦”为例,名字英文字符为 ZHOUJIELUN,字符重复出现时以第一次出现的字符为准,后面重复字符不再入列。依据此规则,字符数组为labels = ['Z','H','O','U','J', 'I', 'E', 'L', 'N'];

(2)依次取 ID_Number 的每两位数作为字符出现的频率,若所有数字都已占用,则进行第二轮赋值,每次只取一个非零数字作为剩余字符字频;如一组 ID_Number 为:421088195202042287,'Z'字频为 42,'H'字频为 10,'O'为 88 等;

# 定义节点类
class Node:
    def __init__(self, char, freq):
        self.char = char  # 节点代表的字符
        self.freq = freq  # 节点代表的字符的频率
        self.left = None  # 左子节点
        self.right = None  # 右子节点

    def __lt__(self, other):
        return self.freq < other.freq

# 构建霍夫曼树
def build_huffman_tree(freq_dict):
    heap = []
    # 将每个字符作为一个节点插入小根堆,根据频率排序(实现了Node类中的__lt__方法)
    for char, freq in freq_dict.items():
        node = Node(char, freq)
        heapq.heappush(heap, node)

    while len(heap) > 1:
        # 从小根堆中取出频率最小的两个节点
        node1 = heapq.heappop(heap)
        node2 = heapq.heappop(heap)

        # 合并这两个节点,生成一个新节点作为它们的父节点
        merged_freq = node1.freq + node2.freq
        merged_node = Node(None, merged_freq)
        merged_node.left = node1
        merged_node.right = node2

        # 将合并后的节点插入小根堆
        heapq.heappush(heap, merged_node)

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

# 生成霍夫曼编码
def generate_huffman_codes(root):
    codes = {}

    # 递归函数,生成每个字符的哈夫曼编码并存储在codes字典中
    def traverse(node, code):
        if node.char:
            # 如果当前节点代表一个字符,则将编码存储到codes字典中
            codes[node.char] = code
        else:
            # 否则,遍历左子树和右子树
            traverse(node.left, code + '0')  # 左子树编码添加'0'
            traverse(node.right, code + '1')  # 右子树编码添加'1'

    # 从二叉树的根节点开始遍历
    traverse(root, '')
    # 返回每个字符的哈夫曼编码
    return codes

# 主函数
def huffman_coding(name, id_number):
    # 生成字符频率字典
    freq_dict = {}
    for char in name:
        if char not in freq_dict:
            # 取id_number的前两位作为字符出现频率
            freq_dict[char] = int(id_number[:2])
            id_number = id_number[2:]
            if len(id_number) == 0:
                break

    # 所有数字已占用,则进行第二轮赋值
    if len(freq_dict) < len(name):
        for char in name:
            if char not in freq_dict:
                # 取id_number的前两位作为字符出现频率
                freq_dict[char] = int(id_number[:2])
                id_number = id_number[2:]
                if len(id_number) == 0:
                    break

    # 构建霍夫曼树
    huffman_tree = build_huffman_tree(freq_dict)

    # 生成霍夫曼编码
    huffman_codes = generate_huffman_codes(huffman_tree)

    # 输出结果
    print("Character\tFrequency\tHuffman Code")
    for char in freq_dict:
        print(f"{char}\t\t{freq_dict[char]}\t\t{huffman_codes[char]}")

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值