本文介紹了有關數組的算法第二部分的Java
代碼實現,全部代碼都可經過 在線編譯器 直接運行,算法目錄:java
k
個數即尋找一列數中最小的k
個數面試
利用最大堆的特色,加入咱們對一個長度爲N
的數組p
的前k
個元素進行建堆操做,那麼p[0]
爲p[0,..,k-1]
的最大值,以後再對p[k,..,N-1]
依次遍歷:算法
p[i]
小於p[0]
,那麼就說明它屬於p[0,..,i]
最小的K
個元素之一,而p[0]
則必定不屬於p[0,..,N-1]
的最小的k
個元素,此時將p[i]
和p[0]
交換,從新對[0,..,N-1]
的部分進行建堆操做p[i]
大於等於p[0]
,那麼就說明p[i]
必定不屬於p
中最小的k
個元素,所以忽略直到i
遍歷到N-1
爲止,此時p[0,..,k-1]
就是數組p
最小的K
個元素。api
class Untitled {
static void maxHeapify(int p[], int di, int length){
int li = (di<<1)+1;
int t = p[di];
while (li < length) {
if (li+1 < length && p[li+1] > p[li])
li++;
if (p[di] >= p[li])
break;
p[di] = p[li];
di = li;
li = (di<<1)+1;
}
p[di] = t;
}
static void buildMaxHeap(int p[], int length){
for(int i=(length>>1)-1; i >= 0; i--){
maxHeapify(p, i, length);
}
}
static void minKthNum(int p[] ,int k, int length) {
buildMaxHeap(p,k);
int t;
for (int i=k; i < length; i++) {
if (p[i] < p[0]) {
t = p[0]; p[0] = p[i]; p[i] = t;
maxHeapify(p, 0, k);
}
}
}
public static void main(String[] args) {
int p[] = new int[]{2, 3, 10, 2, 5, 6, 7, 20, 1, -5};
minKthNum(p, 3, p.length);
for (int i=0; i < 3; i++) {
System.out.println(String.valueOf(p[i]));
}
}
}
複製代碼
>> 2
>> 1
>> -5
複製代碼
數組中的元素有正有負,在該數組中找出一個連續子數組,要求該連續子數組中各元素的和最大,這個連續子數組便被稱做最大連續子數組。好比數組{2,4,-7,5,2,-1,2,-4,3}
的最大連續子數組爲{5,2,-1,2}
,最大連續子數組的和爲5+2-1+2=8
。數組
經過對數組中的元素進行線性的遍歷,並對每一個元素進行累加,當發現目前爲止累加的和maxendinghere
小於0
時,則說明最大的連續子數組不可能包含目前遍歷到的子數組,那麼就從下一個元素tmaxbegin
開始計算子數組。bash
在遍歷的過程當中更新目前位置得到的最大連續子數組的和maxsofar
,以及起止位置maxbegin
和maxend
。ui
class Untitled {
static void maxSumSubArray(int p[], int length){
int maxendinghere = 0;
int maxsofar = 0;
int maxbegin = 0;
int maxend = 0;
int tmaxbegin = 0;
//從0開始遍歷數組。
for(int i = 0; i < length; i++){
//maxendinghere 記錄當前計算子數組的和。
maxendinghere += p[i];
//若是該和小於0,那麼從新計算。
if(maxendinghere < 0){
maxendinghere = 0;
tmaxbegin = i+1;
}
//更新目前爲止計算到的最大值。
if(maxsofar < maxendinghere){
maxbegin = tmaxbegin;
maxend = i;
maxsofar = maxendinghere;
}
}
//考慮數組所有是負數的狀況
if(maxsofar == 0){
maxsofar = p[0];
for(int i = 1; i < length; i++){
if(p[i] > maxsofar){
maxsofar = p[i];
maxbegin = i;
maxend = i;
}
}
}
for (int i = maxbegin; i <= maxend; i++) {
System.out.println("i=" + p[i]);
}
}
public static void main(String[] args) {
int p[] = new int[]{2,4,-7,5,2,-1,2,-4,3};
maxSumSubArray(p, p.length);
}
}
複製代碼
> i=5
> i=2
> i=-1
> i=2
複製代碼
這個問題實際上是2.2
的變種,這時候輸入是一個二維的矩陣,須要找到一個子矩陣,該子矩陣的和是這個二維的全部子矩陣中最大的。spa
二維的問題和2.2
中的一維問題的核心解決思路相同。code
對於二維狀況,咱們將 同一列的多個元素合併成一個元素 來實現降維的效果,爲了能實如今O(1)
的時間內計算出同一列的多行元素之和,須要構建一個輔助數組,該輔助數組ps[i][j]
的值,等於原輸入數組p
以p[0][0]
爲左上角到p[i][j]
爲右下角構成的子矩陣的全部元素之和,經過該輔助數組就能在O(1)
的時間內計算出lRow
到hRow
行之間第col
列的全部元素之和,計算公式爲:排序
ps[hRow][col] - ps[hRow][col-1] - ps[lRow-1][col] + ps[lRow-1][col-1]
複製代碼
class Untitled {
//計算從lRow到hRow之間,位於第col列的全部元素之和。
static int getColsum(int ps[][], int lRow, int hRow, int col) {
return ps[hRow][col] - ps[hRow][col-1] - ps[lRow-1][col] + ps[lRow-1][col-1];
}
static void maxSumSubArray2(int p[][], int xlen, int ylen){
int maxendinghere = 0;
int maxsofar = 0;
int tColbegin = 1;
int sx = 0;
int sy = 0;
int ex = 0;
int ey = 0;
//初始化輔助數組,ps[i][j]爲以其爲右下角的子矩陣的全部元素之和。
int psxLen = xlen+1;
int psyLen = ylen+1;
int[][] ps = new int[psxLen][psyLen];
for (int j = 0; j < psyLen; j++)
ps[0][j] = 0;
for (int i = 0; i < psxLen; i++)
ps[i][0] = 0;
for (int i = 1; i < psxLen; i++) {
for(int j = 1; j < psyLen; j++) {
ps[i][j] = ps[i-1][j] + ps[i][j-1] - ps[i-1][j-1] + p[i-1][j-1];
}
}
//求矩陣中的最大和,將位於同一個列的多行元素合併成一個元素,所以須要遍歷包含不一樣行的狀況。
for (int pStartRow = 1; pStartRow < psxLen; pStartRow++) {
for (int pEndRow = pStartRow; pEndRow < psxLen; pEndRow++) {
for (int pCol = 1; pCol < psyLen; pCol++) {
maxendinghere += getColsum(ps, pStartRow, pEndRow, pCol);
if (maxendinghere < 0) {
maxendinghere = 0;
tColbegin = pCol+1;
}
if (maxsofar < maxendinghere) {
maxsofar = maxendinghere;
sx = pStartRow - 1;
sy = tColbegin - 1;
ex = pEndRow - 1;
ey = pCol - 1;
}
}
maxendinghere = 0;
tColbegin = 1;
}
}
System.out.println("最大和=" + maxsofar + ",起始行=" + sx + ",終止行=" + ex + ",起始列=" + sy + ",終止列=" + ey);
}
public static void main(String[] args) {
int[][] p = {{1,-10,-11}, {4,5,6}, {7,8,9}};
maxSumSubArray2(p, 3, 3);
}
}
複製代碼
>> 最大和=39,起始行=1,終止行=2,起始列=0,終止列=2
複製代碼
在數組中的兩個數字,若是 前面一個數字大於後面的數字,則這兩個數字組成一個 逆序對。輸入一個數組,求出這個數組中的逆序對的總數。
這裏採用的是 歸併算法 的思想,歸併算法包含三個關鍵步驟:
n
的待排序列分解成兩個長度爲n/2
的序列。1
時,序列自己有序,中止遞歸。對於上面的例子,咱們將整個數組分解爲A、B
兩部分,則整個數組的逆序對個數就等於:
A部分組成的數組的逆序對 + B部分組成的數組的逆序對 + A與B之間的逆序對
複製代碼
這裏有一個關鍵的點,就是須要保證在計算A
與B
之間的逆序對時,A
和B
內的元素都是有序的。
class Untitled {
static int inversePairs(int p[], int startIndex, int endIndex) {
if (endIndex == startIndex) {
return 0;
}
if (endIndex-startIndex == 1) {
if (p[endIndex] < p[startIndex]) {
int temp = p[startIndex];
p[startIndex] = p[endIndex];
p[endIndex] = temp;
return 1;
} else {
return 0;
}
}
int midOffset = (endIndex-startIndex) >> 1;
int l = inversePairs(p, startIndex, startIndex+midOffset);
int r = inversePairs(p, startIndex+midOffset+1, endIndex);
return l + r + inverseCore(p, startIndex, midOffset, endIndex);
}
static int inverseCore(int p[], int startIndex, int midOffset, int endIndex) {
int totalLen = endIndex-startIndex+1;
int lLen = midOffset+1;
int rLen = totalLen-lLen;
int l[] = new int[lLen+1];
int r[] = new int[rLen+1];
int i = 0;
for (i=0; i<lLen; i++) {
l[i] = p[startIndex+i];
}
l[i] = 1 << 30;
for (i=0; i<rLen; i++) {
r[i] = p[startIndex+lLen+i];
}
r[i] = 1 << 30;
int c = 0;
i = 0;
int m = 0;
int n = 0;
while(i < totalLen) {
if (r[n] <= l[m]) {
p[startIndex+i] = r[n];
c += (lLen - m);
n++;
i++;
} else {
p[startIndex+i] = l[m];
m++;
i++;
}
}
return c;
}
public static void main(String[] args) {
int[] p = {7,5,6,4};
System.out.println("Inverse Count=" + inversePairs(p, 0, 3));
}
}
複製代碼
>> Inverse Count=5
複製代碼