數組問題彙總
- 1 前言
- 2 題目
-
- 2.1 調整數組順序使奇數位于偶數前面
-
- 2.1.1 思路1
- 2.1.2 代碼1
- 2.1.3 思路2
- 2.1.4 代碼2
- 2.2 數組中出現次數超過一半的數字
-
- 2.2.1 思路1
- 2.2.2 代碼1
- 2.2.3 思路2
- 2.2.4 代碼2
- 2.3 連續子數組的最大和
-
- 2.3.1 思路1
- 2.3.2 代碼1
- 2.3.3 思路2
- 2.3.4 代碼2
- 2.4 把數組排成最小的數
-
- 2.4.1 思路
- 2.4.2 代碼
- 2.5 數組中的逆序對
-
- 2.5.1 題目
- 2.5.2 思路1
- 2.5.3 代碼1
- 2.5.4 思路2
- 2.5.5 代碼2
- 2.6 數字在排序數組中出現的次數
-
- 2.6.1 題目
- 2.6.2 思路1
- 2.6.3 代碼1
- 2.6.4 思路2
- 2.6.5 代碼2
- 2.7 數組中隻出現一次的數字
-
- 2.7.1 題目
- 2.7.2 思路1
- 2.7.3 代碼1
- 2.7.4 思路2
- 2.8 數組中重複的數字
-
- 2.8.1 題目
- 2.8.2 思路1
- 2.8.3 代碼1
- 2.9 建構乘積數組
- 2.9.1 題目描述
- 2.9.2 思路1
- 2.9.3 代碼1
- 3 參考
1 前言
2 題目
2.1 調整數組順序使奇數位于偶數前面
輸入一個整數數組,實作一個函數來調整該數組中數字的順序,使得所有的奇數位于數組的前半部分,所有的偶數位于數組的後半部分,并保證奇數和奇數,偶數和偶數之間的相對位置不變。
2.1.1 思路1
思路:
- 周遊所有元素 然後用兩個空list分别存取奇數和偶數!
- 最後拼接兩個list 奇數在前面!
2.1.2 代碼1
# -*- coding:utf-8 -*-
class Solution:
def reOrderArray(self, array):
# write code here
alist = [] # 存取奇數
blist = [] # 存取偶數
for i in array:
if i%2 == 0:
# 表明為偶數
blist.append(i)
else:
# 表明為奇數
alist.append(i)
return alist + blist
2.1.3 思路2
隻用一個list實作!
- 即遇到奇數在list前面插入insert
- 遇到偶數在list後面append
2.1.4 代碼2
# -*- coding:utf-8 -*-
class Solution:
def reOrderArray(self, array):
# write code here
res = []
l = len(array)
for i in range(l):
if array[l-i-1] % 2 != 0:
res.insert(0,array[-i-1])
if array[i] % 2 == 0:
res.append(array[i])
return res
2.2 數組中出現次數超過一半的數字
數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度為9的數組{1,2,3,2,2,2,5,4,2}。由于數字2在數組中出現了5次,超過數組長度的一半,是以輸出2。如果不存在則輸出0。
2.2.1 思路1
思路:
- 首先求出數組長度
- 其次統計每個數字出現的次數!昨天好像有一個方法實作了!回去看一下!想起來了!直接使用
函數+count
函數filter
2.2.2 代碼1
# -*- coding:utf-8 -*-
class Solution:
def MoreThanHalfNum_Solution(self, numbers):
# write code here
# 清單長度
n = len(numbers) / 2
a = list(set(list(filter(lambda x: numbers.count(x) > n, numbers))))
if len(a) == 0:
return 0
else:
return a[0]
2.2.3 思路2
先排序!
- 使用快速排序!然後如果某一個數字的出現次數大于數組一半的長度,那麼中間的數肯定就是它了!
- 然後統計中間數出現了多少次!大于一半則ok!
- 注意考慮邊界情況!隻有一個元素的list!
- 快排時間複雜度為 O ( N l o g N ) O(NlogN) O(NlogN)
2.2.4 代碼2
class Solution:
def quick_sort(self, alist, first, last):
# 快排
if first >= last:
return
# 定義兩個遊标+中值
low = first
high = last
mid = alist[first]
while low < high:
while alist[high] >= mid and high > low:
high -= 1
alist[low] = alist[high]
while alist[low] < mid and low < high:
low += 1
alist[high] = alist[low]
alist[low] = mid
# 遞歸
self.quick_sort(alist, first, low-1)
self.quick_sort(alist, low+1, last)
return alist
def MoreThanHalfNum_Solution(self, numbers):
# write code here
# 考慮特殊情況
if len(numbers) == 1:
return numbers[0]
# 首先快速排序
alist = self.quick_sort(numbers, 0, len(numbers)-1)
print(alist)
# 判斷-看中值出現的次數是否大于一半!因為如果有 肯定中值為出現最多的之一!否則不可能!
n = int(len(alist)/2)
# 中值為:
mid = alist[n]
# 看有多少個中值!
count = 0
for j in alist:
if j == mid:
count += 1
if count > len(alist)/2 :
return mid
else:
return 0
2.3 連續子數組的最大和
HZ偶爾會拿些專業問題來忽悠那些非計算機專業的同學。今天測試組開完會後,他又發話了:在古老的一維模式識别中,常常需要計算連續子向量的最大和,當向量全為正數的時候,問題很好解決。但是,如果向量中包含負數,是否應該包含某個負數,并期望旁邊的正數會彌補它呢?例如:{6,-3,-2,7,-15,1,2,2},連續子向量的最大和為8(從第0個開始,到第3個為止)。給一個數組,傳回它的最大連續子序列的和,你會不會被他忽悠住?(子向量的長度至少是1)
2.3.1 思路1
- 如何考慮呢?
- 肯定沒法暴力去搜尋啊,因為組合太多了,肯定搞不過來的!那有沒有簡單的方法呢?判斷正負号?或者說有什麼規律或者結論?真想不到!
一個牛逼的思路:

即遇到求和為負數則重新規整為0開始!如果一開始就是負數也添加到全局list中,以防全部為負數!(特殊情況)
2.3.2 代碼1
# -*- coding:utf-8 -*-
class Solution:
def FindGreatestSumOfSubArray(self, array):
# write code here
# 法1 找到了規律!
n = len(array)
if n==1:
return array[0]
res = []
s = 0
for i in array:
s += i
if s > 0:
# 如果累加和大于0 append到res 繼續
res.append(s)
else:
# 如果累加和小于0 s歸為0 重新開始 并添加到res
res.append(s)
s = 0
return max(res)
2.3.3 思路2
每一步求和 然後和之前的和取max!!!哇 這個思路牛逼啊!但是有一個問題!怎麼斷掉前面累加和為負數的情況呢!使用動态規劃:
以 a r r a y = [ 1 , − 2 , 3 , 10 , − 4 , 7 , 2 , − 5 ] array=[1,-2,3,10,-4,7,2,-5] array=[1,−2,3,10,−4,7,2,−5]為例:
首先定義相關符号:
- F ( i ) F(i) F(i):以 a r r a y [ i ] array[i] array[i]為末尾元素的子數組的和的最大值,子數組的元素的相對位置不變
- F ( i ) = m a x ( F ( i − 1 ) + a r r a y [ i ] , a r r a y [ i ] ) F(i)=max(F(i-1)+array[i] , array[i]) F(i)=max(F(i−1)+array[i],array[i])
- r e s res res:所有子數組的和的最大值
- r e s = m a x ( r e s , F ( i ) ) res=max(res,F(i)) res=max(res,F(i))
開始疊代進行:
- 第一步, F ( 0 ) = m a x ( F ( 0 ) + a r r a y [ 0 ] , a r r a y [ 0 ] ) = 1 F(0)=max(F(0)+array[0],array[0])=1 F(0)=max(F(0)+array[0],array[0])=1, r e s = m a x ( r e s , F ( 0 ) ) = 1 res=max(res,F(0))=1 res=max(res,F(0))=1
- 第二步, F ( 1 ) = m a x ( F ( 0 ) + a r r a y [ 1 ] , a r r a y [ 1 ] ) = m a x ( 1 − 2 , − 2 ) = − 1 F(1)=max(F(0)+array[1],array[1])=max(1-2,-2)=-1 F(1)=max(F(0)+array[1],array[1])=max(1−2,−2)=−1, r e s = m a x ( r e s , F ( 1 ) ) = 1 res=max(res,F(1))=1 res=max(res,F(1))=1
- 第三步, F ( 2 ) = m a x ( F ( 1 ) + a r r a y [ 2 ] , a r r a y [ 2 ] ) = m a x ( − 1 + 3 , 3 ) = 3 F(2)=max(F(1)+array[2],array[2])=max(-1+3,3)=3 F(2)=max(F(1)+array[2],array[2])=max(−1+3,3)=3, r e s = m a x ( r e s , F ( 2 ) ) = 3 res=max(res,F(2))=3 res=max(res,F(2))=3 這一步就能有效的實作斷掉前面累加和為負數的情況!也是為什麼也要把最近的一個數也作為比較的對象,比較的就是前面所有的累加和!
- 第四步, F ( 3 ) = m a x ( F ( 2 ) + a r r a y [ 3 ] , a r r a y [ 3 ] ) = m a x ( 3 + 10 , 10 ) = 13 F(3)=max(F(2)+array[3],array[3])=max(3+10,10)=13 F(3)=max(F(2)+array[3],array[3])=max(3+10,10)=13, r e s = m a x ( r e s , F ( 3 ) ) = 13 res=max(res,F(3))=13 res=max(res,F(3))=13
- 第五步, F ( 4 ) = m a x ( F ( 3 ) + a r r a y [ 4 ] , a r r a y [ 4 ] ) = m a x ( 13 − 4 , − 4 ) = 9 F(4)=max(F(3)+array[4],array[4])=max(13-4,-4)=9 F(4)=max(F(3)+array[4],array[4])=max(13−4,−4)=9, r e s = m a x ( r e s , F ( 4 ) ) = 13 res=max(res,F(4))=13 res=max(res,F(4))=13
- 第六步, F ( 5 ) = m a x ( F ( 4 ) + a r r a y [ 5 ] , a r r a y [ 5 ] ) = m a x ( 9 + 7 , 7 ) = 16 F(5)=max(F(4)+array[5],array[5])=max(9+7,7)=16 F(5)=max(F(4)+array[5],array[5])=max(9+7,7)=16, r e s = m a x ( r e s , F ( 4 ) ) = 16 res=max(res,F(4))=16 res=max(res,F(4))=16
- 第七步, F ( 6 ) = m a x ( F ( 5 ) + a r r a y [ 6 ] , a r r a y [ 6 ] ) = m a x ( 16 + 2 , 2 ) = 18 F(6)=max(F(5)+array[6],array[6])=max(16+2,2)=18 F(6)=max(F(5)+array[6],array[6])=max(16+2,2)=18, r e s = m a x ( r e s , F ( 5 ) ) = 18 res=max(res,F(5))=18 res=max(res,F(5))=18
- 第八步, F ( 7 ) = m a x ( F ( 6 ) + a r r a y [ 7 ] , a r r a y [ 7 ] ) = m a x ( 18 − 5 , − 5 ) = 13 F(7)=max(F(6)+array[7],array[7])=max(18-5,-5)=13 F(7)=max(F(6)+array[7],array[7])=max(18−5,−5)=13, r e s = m a x ( r e s , F ( 6 ) ) = 18 res=max(res,F(6))=18 res=max(res,F(6))=18
綜上!最大的子數組和為18!
2.3.4 代碼2
開始結合上面思路寫代碼:
# -*- coding:utf-8 -*-
class Solution:
def FindGreatestSumOfSubArray(self, array):
# write code here
# 法1 找到了規律!
n = len(array)
if n==1:
return array[0]
s = 0
l = 0
res = float("-inf") # 不能取為0 因為可能全為負數!這樣就會出現bug
for i in array:
# l為中間過渡變量 表示遞歸的前n項最大和
l = s+i
# s為前多少項和 和 最近一個值的最大值!
s = max(l, i)
res = max(res,s)
return res
2.4 把數組排成最小的數
輸入一個正整數數組,把數組裡所有數字拼接起來排成一個數,列印能拼接出的所有數字中最小的一個。例如輸入數組{3,32,321},則列印出這三個數字能排成的最小數字為321323。
2.4.1 思路
- 拼接數字!還是看這個數字的個數位 十位的一個情況!多個數字比較深度!
- 但落實到代碼層面怎麼去考慮呢?不知道 有點懵逼啊!能不能暴力搜尋一下?n的階乘種排列方式!太複雜了!
牛逼的思路:
- 将每個數字都變成字元串!然後将字元串進行拼接成不同的數 然後再直接取字元串最小的!輸出對應的值即可!
2.4.2 代碼
# -*- coding:utf-8 -*-
class Solution:
def PrintMinNumber(self, numbers):
# write code here
if len(numbers) == 0:
return ''
compare = lambda a, b:cmp(str(a) + str(b), str(b) + str(a))
min_string = sorted(numbers, cmp = compare)
return ''.join(str(s) for s in min_string)
2.5 數組中的逆序對
2.5.1 題目
在數組中的兩個數字,如果前面一個數字大于後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數P。并将P對1000000007取模的結果輸出。 即輸出P%1000000007
2.5.2 思路1
直接暴力搜!
- 首先兩兩比較值的大小,求和逆序對總數P
2.5.3 代碼1
# -*- coding:utf-8 -*-
class Solution:
def InversePairs(self, data):
# write code here
# 暴力搜尋
n = len(data)
count = 0
for i in range(n):
for j in range(i+1,n):
if data[i] > data[j]:
count += 1
return count%1000000007
思路簡單,代碼也簡單,但是時間複雜度無法通過,為平方複雜度!想辦法優化!
2.5.4 思路2
太炫的思路了。
結論:逆序對的總數 = 左邊數組中的逆序對的數量 + 右邊數組中逆序對的數量 + 左右結合成新的順序數組時(注意:左右先進行升序排列!)中出現的逆序對的數量
為什麼呢?其實也很簡單!
- 首先分成兩部分沒問題,分别統計左右兩邊數組逆序對的數量
- 現在需要合并!但合并之後需要先對左右兩邊進行排序!因為防止左右兩邊内部再加一次!
- 是以就是歸并排序的思想!分治法!
- 但代碼還是有點難度的~
2.5.5 代碼2
# -*- coding:utf-8 -*-
class Solution:
def InversePairs(self, data):
# write code here
if not data:
return 0
temp = [i for i in data]
return self.mergeSort(temp, data, 0, len(data)-1) % 1000000007
def mergeSort(self, temp, data, low, high):
if low >= high:
temp[low] = data[low]
return 0
mid = (low + high) / 2
left = self.mergeSort(data, temp, low, mid)
right = self.mergeSort(data, temp, mid+1, high)
count = 0
i = low
j = mid+1
index = low
while i <= mid and j <= high:
if data[i] <= data[j]:
temp[index] = data[i]
i += 1
else:
temp[index] = data[j]
count += mid-i+1
j += 1
index += 1
while i <= mid:
temp[index] = data[i]
i += 1
index += 1
while j <= high:
temp[index] = data[j]
j += 1
index += 1
return count + left + right
2.6 數字在排序數組中出現的次數
2.6.1 題目
統計一個數字在排序數組中出現的次數。
2.6.2 思路1
- 直接循環周遊判斷
- 但加上一個條件 如果位置i等于一個值 但位置i+1不等于 那麼就break 因為數組是排序好的!
2.6.3 代碼1
# -*- coding:utf-8 -*-
class Solution:
def GetNumberOfK(self, data, k):
# write code here
# 使用簡單的方法 循環周遊
cnt = 0
for i in range(len(data)):
if data[i] == k:
cnt += 1
elif data[i] == k and data[i+1] != k:
break
else:
continue
return cnt
2.6.4 思路2
- 二分查找!這樣的複雜度會比上面的低,上面的複雜度為O(n)
- 首先通過二分查找找到該元素 然後再以該元素為中心分别向兩邊去擴散!去找 看有多少相同的!
2.6.5 代碼2
遇到的坑:
- 首先是二分查找得需要非遞歸版本 這樣才能傳回該找到的元素的位置
- 注意考慮邊界條件,即這個元素可能不存在!是以要先寫一個判斷!
- 注意将mid進行回歸!矯正到中值的位置!
# -*- coding:utf-8 -*-
class Solution:
def Binary_search(self, data, k):
# 二分查找
n = len(data)-1
start = 0
end = n
while start <= end:
mid = (start + end) // 2
if data[mid] < k:
# right
start = mid + 1
elif data[mid] > k:
# left
end = mid - 1
else:
return mid
return -1
def GetNumberOfK(self, data, k):
# write code here
# 使用二分查找
mid = self.Binary_search(data, k)
if mid == -1:
return 0
else:
cnt = 1
while mid + 1 <= len(data)-1 and data[mid+1] == k:
# 防止越界 + 符合條件
mid += 1
cnt += 1
mid_org = mid - cnt + 1
while mid_org - 1 >= 0 and data[mid_org - 1] == k:
# 防止越界 + 符合條件
mid_org -= 1
cnt += 1
return cnt
2.7 數組中隻出現一次的數字
2.7.1 題目
一個整型數組裡除了兩個數字之外,其他的數字都出現了兩次。請寫程式找出這兩個隻出現一次的數字。
2.7.2 思路1
- 暴力解法!直接周遊!
- 結果存入到字典!最後周遊字典即可!
- 注意下标!!!
- 以及注意字典判斷某個key是否存在的方法
2.7.3 代碼1
# -*- coding:utf-8 -*-
class Solution:
# 傳回[a,b] 其中ab是出現一次的兩個數字
def FindNumsAppearOnce(self, array):
# write code here
res = []
dic = {}
for i in range(len(array)):
#if array[i] in dic:
if array[i] in dic:
dic[array[i]] += 1
else:
dic[array[i]] = 0
for k,v in dic.items():
if v == 0:
res.append(k)
return res
2.7.4 思路2
- 先排序!然後統計 如果次數為1
- 好像不太行的!!!
- 用的異或的思路!但是代碼太複雜了!忽略!
2.8 數組中重複的數字
2.8.1 題目
在一個長度為n的數組裡的所有數字都在0到n-1的範圍内。 數組中某些數字是重複的,但不知道有幾個數字是重複的。也不知道每個數字重複幾次。請找出數組中任意一個重複的數字。 例如,如果輸入長度為7的數組{2,3,1,0,2,5,3},那麼對應的輸出是第一個重複的數字2。
2.8.2 思路1
- 周遊!用一個字典來判斷!key為值 value為出現的次數
- 如果最後次數大于1則break!
- 記得加一個cnt來判斷是否進入了某一個循環!
2.8.3 代碼1
# -*- coding:utf-8 -*-
class Solution:
# 這裡要特别注意~找到任意重複的一個值并指派到duplication[0]
# 函數傳回True/False
def duplicate(self, numbers, duplication):
# write code here
dic = {}
cnt = 0
for num in numbers:
if num in dic:
dic[num] += 1
if dic[num] == 2:
cnt += 1
duplication[0] = num
break
else:
continue
else:
dic[num]=1
if cnt != 0:
return True
else:
return False
2.9 建構乘積數組
2.9.1 題目描述
給定一個數組A[0,1,…,n-1],請建構一個數組B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。
2.9.2 思路1
- 直接周遊!分兩段來!注意把每次循環空出一個位置來!!!
- 另外要注意不能一開始空list 然後直接指派!隻有append!或者說一開始賦予初始值 然後在這個基礎之上修改即可!
2.9.3 代碼1
# -*- coding:utf-8 -*-
class Solution:
def multiply(self, A):
# write code here
n = len(A)
B = list(range(n))
# 基于分割點分别乘積得到結果
for i in range(n):
B[i] = 1
# 第一段
for j in range(i):
B[i] *= A[j]
# 第二段
for k in range(i+1, n):
B[i] *= A[k]
return B
3 參考
- https://blog.csdn.net/c406495762/article/details/79247243