1. 程式人生 > >python每日經典演算法題5(基礎題)+1(中難題)

python每日經典演算法題5(基礎題)+1(中難題)

  現在,越來越多的公司面試以及考驗面試對演算法要求都提高了一個層次,從現在,我講每日抽出時間進行5+1演算法題講解,5是指基礎題,1是指1道中等偏難。希望能夠讓大家熟練掌握python的語法結構已經一些高階函式的應用。這些題目是在某些刷題的網站上登記的有水平的題目。這裡如果有需要input的簡單題,就略去了輸出結果。如果時間充裕,則就會增加每日更多習題。

 

一:基礎演算法題10道

1.判斷使用者輸入的年份是否為閏年

題目解析:

(1)問題分析:能被4整除但不能被100整除的年份為普通閏年,能被400整除的年份為世紀閏年,判斷是否滿足上述情況。

(2)演算法分析:輸入一個數,先判斷如果是400的倍數,則滿足;如果不是400的倍數,再判斷如果該數能夠被4整除,卻不能被100整除,則滿足。

(3)用到的python語法:input()標準輸入函式,if判斷語句,or,and邏輯運算子。

(4)博主答題程式碼:

n = int(input('請輸入年份:'))
if n % 400 == 0 or n % 4 == 0 and n % 100 != 0:
    print('{0}是閏年'.format(n))
else:
     print('{0}不是閏年'.format(n))

(5)高效方法:
python 的 calendar 庫中已經封裝好了一個方法 isleap() 來實現這個判斷是否為閏年:

import calendar

n = int(input("請輸入年份:"))
year = calendar.isleap(n)
if year == True:
    print ("{0}是閏年".format(n))
else:
    print ("{0}不是閏年".format(n))

 

2.判斷一個數是否是質數

題目解析:

(1)問題分析:除了1和它本身以外不再有其他的因數的數就是質數。

(2)演算法分析:輸入一個數,如果該數大於1,則從2開始迴圈到該數並一一整除該數,如果餘數為0,則該數不是質數;否則該數是質數。

(3)用到的python語法:input()標準輸入函式,for迴圈,if判斷語句。

(4)博主答題程式碼:

n = int(input(''))
if n > 1:
    for i in range(2,n):
        if n % i == 0:
            print('{0}不是質數'.format(n))
            print('{n}={a}*{b}'.format(n=n,a=i,b=int(n/i)))    # 這裡也可為b=n//i
            break
        else:
            print('{0}是質數'.format(n))
            break

這時如果想把非質數的所有非1與自己的因數輸出,則可以改為如下程式碼:

n = int(input(''))
if n > 1:
    m1 = 0;m2 = 0
    for i in range(2,n):
        if n % i == 0:
            str = '{0}不是質數'.format(n)
            print('{n}={a}*{b}'.format(n=n,a=i,b=int(n/i)))    # 這裡也可為b=n//i
            m1 = 1;m2 = 1
        elif m1==0:
            print('{0}是質數'.format(n))
            break
    if [m1,m2].count(1) == 2:
        print('{0}不是質數'.format(n))

(5)高效方法:

num = int(input(""))
i = 2
while i < num:
    s = num % i
    if s == 0:
        break
    else:
        i += 1
if num == i:
    print("{0}是質數".format(num))
else:
    print("{0}不是質數".format(num))

 

3.輸出指定範圍內的素數

(1)題目分析:素數就是質,上一題已經介紹瞭如何求質數,這裡我們需要加一個範圍。
(2)演算法分析:把上一題判斷的內容放在一個for迴圈選擇範圍裡進行分析。

(3)用到的python語法:input()標準輸入函式,map函式,for迴圈,if判斷語句。
(4)博主答題程式碼:

my_index1,my_index2 = map(int,input('請選擇一個範圍:').split(','))
result = []

for num in range(my_index1,my_index2+1):
    if num > 1:
        for i in range(2,num):
            if (num % i) == 0:
                break
        else:
            result.append(num)
print('{a}到{b}所有的質數有:{c}'.format(a = my_index1,b = my_index2,c = result))

這裡的if和else是就近匹配,和上面的不同,這就避免了重複,這是要注意的一點。

展示這是最簡單的方法,如果大家有好的方法,請評論。

 

4.約瑟夫生者死者小遊戲

30 個人在一條船上,超載,需要 15 人下船。

於是人們排成一隊,排隊的位置即為他們的編號。

報數,從 1 開始,數到 9 的人下船。

如此迴圈,直到船上僅剩 15 人為止,問都有哪些編號的人下船了呢?

這一題可以擴充套件為:

m個人在一條船上,超載,需要n人下船。

於是人們排成一隊,排隊的位置即為他們的編號。

報數,從 1 開始,數到 k 的人下船。

如此迴圈,直到船上僅剩 m - n人為止,問都有哪些編號的人下船了呢?

(1)題目分析:

(2)演算法分析:這裡設定編號從1開始,先給出從1到m的所有編號,用一個列表表示,需要n個人下船,則船上就生了m-n個人。以此迴圈遞迴,其實也可以轉化為遞迴問題。但這裡每次遞迴,當找到數到編號k,就把編號k所在的序號,就是編號刪除。這裡可以設定一個外部變數,從第一個編號index開始,當index=k時,把編號重新置為1,每次都這樣,直到迴圈完畢,直到所有最後剩餘元素個數為m-n。

(3)用到的python語法:for迴圈,函式,列表操作,列表切片。

(4)博主答題程式碼:

 

def yueSeFu(m,n,k):
    serial_num = list(range(1,m +1))    # 建立從1到m的序號
    index = 0                            # 設定外部變數index
    while len(serial_num) > m - n:        # 當最後剩餘人數為m - n之前,一直進行下面的程式
        for i in serial_num:            # 遍歷每個編號
            index += 1                     # 把外部變數index進行真實遍歷
            if index == k:                # 當外部變數index找到k時,進行下面程式碼塊的操作
                serial_num.remove(i)    # 移除需要下船的人的編號
                index = 1                # 這時候index已經找到序號k了,就要重新遍歷
                print('{0}號人下船了'.format(i))

if __name__ == '__main__':
    # 傳入起始人數m,需要下船的人數n,數到多少下船的序號k,這裡可自行設定
    yueSeFu(30,15,9)

(5)高效方法:

def yueSeFu(m,n,k):
    people = list(range(1, m+1))
    i = 0
    while len(people) > m - n:
        i += (k - 1)
        if i >= len(people):
            i -= len(people)
        print("{}號下船了".format(people[i]))
        del people[i]

if __name__ == '__main__':
    # 傳入起始人數m,需要下船的人數n,數到多少下船的序號k
    yueSeFu(30,15,9)

 

5.二分查詢,返回某個值在陣列中的索引

(1)題目分析:二分搜尋是一種在有序陣列中查詢某一特定元素的搜尋演算法。從陣列的中間元素開始,正好是要查詢的元素,則搜尋過程結束;如果某一特定元素大於或者小於中間元素,則在陣列大於或小於中間元素的那一半中查詢,而且跟開始一樣從中間元素開始比較。如果在某一步驟陣列為空,則代表找不到。這種搜尋演算法每一次比較都使搜尋範圍縮小一半。

(2)演算法分析:這裡重複查詢需要用到遞迴,用到一個一維陣列,先判斷陣列查詢的元素末尾下標是否大於等於1。如果是的話,就要先找到中間位置,如果大於中間位置,就只比較左邊的元素重新遞迴;如果小於中間位置,則比較右邊的元素重新遞迴。找不到就返回-1。
(3)用到的python語法:if判斷語句,函式,遞迴演算法。

(4)博主答題程式碼:

def twoSearch(x,arr,begin,end):    # x為要查詢的元素,arr為陣列,begin和and是每次查詢的範圍
    if end >= 1:
        mid = int(begin + (end-1)/2)    # 每次範圍的元素中間位置
        if int(arr[mid]) == x:
            return mid
        elif int(arr[mid]) > x:        # 元素小於中間的元素,需要比較左邊的元素
            return twoSearch(x,arr,begin,end - 1)
        else:                    # 元素大於中間的元素,需要比較右邊的元素
            return twoSearch(x,arr,begin + 1,end)
    else:
        return -1

if __name__ == '__main__':
    my_arr = list(map(int,input('請輸入陣列:').split(',')))    # 返回可迭代物件
    my_x = int(input('請輸入要查詢的元素:'))
    result = twoSearch(my_x, my_arr, 0, len(my_arr) - 1)
    if result != -1:
        print('元素在陣列中的索引為:{0}'.format(result))
    else:
        print('元素不在陣列中')

 

二:中等演算法題2道

1.兩數之和

給定一個整數陣列 nums 和一個目標值 target,請你在該陣列中找出和為目標值的那 兩個 整數,並返回他們的陣列下標。

示例:

給定 nums = [2, 7, 11, 15], target = 9

因為 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

(1)演算法解析:我們給出一個列表,進行兩次迴圈,就可以得到結果。
(2)博主程式碼:

def twoSum(nums, target):
    for index1,i in enumerate(nums):
        for index2,j in enumerate(nums):
            if i+j == target and i != j:
                return index1,index2


a = list(map(int,input().split(',')))
b = int(input())
print(list(twoSum(a,b)))

主要程式碼為:

for index1,i in enumerate(nums):
        for index2,j in enumerate(nums):
            if i+j == target and i != j:
                return index1,index2

(3)程式碼問題

  但是在執行很多個數字的列表中,需要兩次迴圈遍歷列表,如果列表的長度為n,則時間複雜度為O(n),時間執行效率太差,最差為O(n^2),故上面的程式碼實際上是不太可取的。

(4)高階演算法

下面有一些改進的高階一點的演算法:

列表生成式三行程式碼搞定:

for i in range(len(nums)):    # 迴圈遍歷列表
        # 這裡是用和減去列表中的某一元素,並且兩個元素下標不同,就返回兩個下標
        if(target-nums[i] in nums and i != nums.index(target-nums[i])):
                return [i,nums.index(target-nums[i])]

執行用時 :1284ms,記憶體消耗 :14.7MB左右,比上面的程式碼節省了一層迴圈遍歷的過程。

字典查詢,利用雜湊表,不用遍歷:

my_dict = {}                        # 建立一個字典
    for index, num1 in enumerate(nums):    # 利用函式enumerate輸出列表或陣列的下標和元素
        num2 = target - num1            # 另一個元素
        # 這裡是判斷,如果字典中有另一個元素值的話,返回下標,以及該元素下標
        # 這裡由於字典是鍵值對,就避免了兩個元素和符合,單元素是同一個元素的情況
        if num2 in my_dict:                
            return [my_dict[num2], index]
        # 把字典中的元素對應於鍵
        my_dict[num1] = index
    return -1

執行用時 :68ms,記憶體消耗 :15 MB左右,時間效率更高。

利用集合進行操作,效率和字典差不多:

my_set = set(nums)
    for i, v in enumerate(nums):
        if (target - v) in my_set and i != nums.index(target - v):
            return [i, nums.index(target - v)]

執行用時 :80ms,記憶體消耗 :15 .2MB左右。

 

總結:

  1.用python字典或集合的效率要高很多,不建議常用列表。

  2.生成一個整數序列可以先生成一個可迭代物件

    比如生成一個只有整數的可迭代物件:

    map(int,input('請選擇一個範圍:').split(','))

  3.calendar.isleap(n可以判斷是否為閏年。

  4.列表生成式常用可以減少程式碼量,當然是有必要用列表的時候。

  5.enumerate對既需要元素和下標值的序列很有