寻找第k小数

一、实验目的
1、掌握寻找第k小的数算法;
2、上机实现该算法。
二、理解算法
在这里插入图片描述
三、函数接口设计

在这里插入图片描述
四、遇到的问题
在写程序时,被这个问题困扰了很久
在这里插入图片描述
经过演算发现,在有些函数里,没有考虑到特殊的情况,像在递归最后,总数不满5个,没办法分成5个一组,而剩下的都是比较不匀称的怎么处理。
在这里插入图片描述
像这里,按照书上的算法,分组小于mm的那部分,下标为[3,5)的都是小于M*(mid)的,但是有一些细节需要处理,如果最后只剩下[2,3,4,5]等一些特殊情况但又是在程序进行中一定会出现的情况,应该怎么处理。
在这里插入图片描述
问题2:当个数小于5时怎么划分

在这里插入图片描述
问题3:递归嵌套后如何返回

五、运行时间分析

在这里插入图片描述
当k=10000时,超出了python中递归次数的限制,采用下面的代码去修改递归次数,
sys.setrecursionlimit(10000000)

当k=1000000,StackOverflow,堆溢出了,由于这是一个递归程序,每一次递归都需要开辟新的内存空间,所以消耗很大。采用如下解决办法:
threading.stack_size(200000000)
thread=threading.Thread(target=select_kth_smallest,args=(arr,10))
thread.start()

我将函数时间画成图表,可以明显看到增长趋势:

在这里插入图片描述

六、实验结果截图
随机产生1000个数,输入要寻找的第k小
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

import math
import random
import datetime
import sys
import threading

def partition(arr, low, high):
    i = (low - 1)  # 最小元素索引
    pivot = arr[high]
    for j in range(low, high):
        # 当前元素小于或等于 pivot
        if arr[j] >= pivot:
            i = i + 1
            arr[i], arr[j] = arr[j], arr[i]
    arr[i + 1], arr[high] = arr[high], arr[i + 1]
    return (i + 1)

# 快速排序函数
def quickSort_FindMid(arr, low, high):
    if low < high:
        pi = partition(arr, low, high)
        quickSort_FindMid(arr, low, pi - 1)
        quickSort_FindMid(arr, pi + 1, high)

def quickSort(arr, low, high):
    if low < high:
        i = (low - 1)  # 最小元素索引
        pivot = arr[high][0]
        for j in range(low, high):
            # 当前元素小于或等于 pivot
            if arr[j][0] <= pivot:
                i = i + 1
                arr[i], arr[j] = arr[j], arr[i]

        arr[i + 1], arr[high] = arr[high], arr[i + 1]
        pi = (i + 1)
        quickSort_FindMid(arr, low, pi - 1)
        quickSort_FindMid(arr, pi + 1, high)

def generate_arr(Num):
    Arr = []
    for i in range(0,Num):
        x = random.randint(0, 20000)
        Arr.append(x)
    return Arr

def part5Sort(midArr,arr):
    arrAfterSort = []
    if len(midArr)>1:
        for i in range(0,len(midArr)):
            if len(arr) - (midArr[i][1]+2)>0:
                left = midArr[i][1] - 2
                right = midArr[i][1] + 2
                arrAfterSort.append(arr[left:right+1])
    #把不能整除5的部分再添加到最后
    t = len(arr)%5
    if t!=0:
        right = len(arr)
        left = right - t

        arrAfterSort.append(arr[left:right])
    print("part5Sort:")
    print(arrAfterSort)
    return arrAfterSort

def devide_to_S(arrAfterSort,mm):
    #mm是第几个分组
    #mid是所指的数
    # S1是比mm小的
    # S2是比mm大的
    S1 = []
    S2 = []
    #如果最后只剩一组
    if len(arrAfterSort) == 1:
        mid = arrAfterSort[0][len(arrAfterSort[0]) // 2]
        mid_index = len(arrAfterSort[0]) // 2
        for j in range(0, len(arrAfterSort[0])):
            if j!=mid_index:
                if (arrAfterSort[0][j] >= mid):
                    S2.append(arrAfterSort[0][j])
                else:
                    S1.append(arrAfterSort[0][j])
        print(S1, S2, mid)
        return (S1, S2,mid)
    else:
        mid = arrAfterSort[mm][2]
        #如果mm前面有一组
        if mm != 0:
            for i in range(0,mm):
                for j in range(0,2):
                    if(arrAfterSort[i][j]>mid):
                        S2.append(arrAfterSort[i][j])
                    else:
                        S1.append(arrAfterSort[i][j])
                for j in range(2,5):
                    S1.append(arrAfterSort[i][j])
        S1.append(arrAfterSort[mm][3])
        S1.append(arrAfterSort[mm][4])

        # 如果mm后面有组是5个一分的
        if mm+1 < len(arrAfterSort)-1:
            #最后一组可能不规则,不满5个,最后特殊处理
            for i in range(mm+1,len(arrAfterSort)-1):
                for j in range(3, 5):
                    if arrAfterSort[i][j] > mid:
                        S2.append(arrAfterSort[i][j])
                    else:
                        S1.append(arrAfterSort[i][j])
                for j in range(0, 3):
                    S2.append(arrAfterSort[i][j])
        S2.append(arrAfterSort[mm][0])
        S2.append(arrAfterSort[mm][1])

        #最后处理不规则组
        k = len(arrAfterSort)-1
        t = len(arrAfterSort[k])
        for j in range(0,t):
            if (arrAfterSort[k][j] > mid):
                S2.append(arrAfterSort[k][j])
            else:
                S1.append(arrAfterSort[k][j])
        print(S1,S2,mid)
        return (S1,S2,mid)


def select_kth_smallest(arr, k):
    midArr = []
    arrAfterSort = []
    S1 = []
    S2 = []
    left = 0
    while left + 4 < len(arr):
        quickSort_FindMid(arr, left, left + 4)
        mid = left+2
        midArr.append([arr[mid],mid])
        left += 5

    if len(arr)%5 != 0:
        quickSort_FindMid(arr,left,len(arr)-1)
        index = (int)((left+len(arr)-1)//2)
        midArr.append([arr[index], index])
        quickSort(midArr,0,len(midArr)-1)
    #quickSort(midArr,0,len(midArr)-1)
    midArr.sort()
    print("midArr")
    print(midArr)
    print(arr)
    arrAfterSort = part5Sort(midArr,arr)
    mm = (len(midArr)-1)//2
    S = devide_to_S(arrAfterSort,mm)
    print(S)
    S1 = S[0]
    S2 = S[1]
    midd = S[2]
    print("k=%d"%k)
    if k==(len(S1)+1):
        return midd
    elif k>len(S1):
        return select_kth_smallest(S2, k-len(S1)-1)
    else:
        return select_kth_smallest(S1, k)


if __name__ == '__main__':
    starttime = datetime.datetime.now()
    n = input("Enter your input number: ")
    n = int(n)
    arr = []
    arr = generate_arr(1000)
    print(arr)
    x = select_kth_smallest(arr,n)
    arr.sort()
    print(arr)
    endtime = datetime.datetime.now()
    print("Time used:", endtime - starttime)
    print("the %d answer:%d"%(n,x))

猜你喜欢

转载自blog.csdn.net/weixin_43118073/article/details/104974797
今日推荐