【歸併排序,同步指標】陣列中的逆序對,兩個連結串列的第一個公共結點
阿新 • • 發佈:2018-12-16
面試題51:陣列中的逆序對
在陣列中的兩個數字如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個陣列中的逆序對的總數。
分成長度1的子陣列,在合併之前統計相鄰子陣列之間的逆序對個數,然後讓它們升序合併,升序的最大的一定在最後面,然後再如此反覆即可。具體圖解見書上。
#include<bits/stdc++.h>
using namespace std;
// 引數:
// data: 一個整數陣列
// copy: 複製陣列
// start: 操作起點下標
// end: 操作終點下標
// 返回值:
// data陣列中從start到end位置逆序對的總數
int InversePairsCore(int* data, int* copy, int start, int end) {
if(start == end) {//遞迴出口:子陣列中僅有一個數字
copy[start] = data[start];//將其直接拷貝到copy陣列
return 0;//只有一個數字,一定不包含逆序對
}
int length = (end - start) / 2;//二分陣列,子陣列的長度
//左側部分/右側部分分別遞迴計算:反過來呼叫則最終有序的在data數組裡
int left = InversePairsCore(copy, data, start, start + length);
int right = InversePairsCore(copy, data, start + length + 1, end);
//i初始化為前半段最後一個數字的下標(前半段最大數)
int i = start + length;
//j初始化為後半段最後一個數字的下標(後半段最大數)
int j = end;
int indexCopy = end;//要複製到的copy陣列的位置下標
int count = 0;//比較過程中發現的逆序對數目
//兩個子陣列中都有數字未複製完
while (i >= start && j >= start + length + 1) {
if(data[i] > data[j]) {//左側陣列中的數更大
copy[indexCopy--] = data[i--];//複製到copy陣列中
//右側子陣列中剩餘的所有數字都和左側陣列中的這個數字構成逆序對
count += j - start - length;//注意start+length才是分割點的位置下標
} else {//右側陣列中的數更大
copy[indexCopy--] = data[j--];
}
}
//上面的&&條件有一邊不滿足就終止,這時左右有一個數組還沒複製完
for(; i >= start; --i)//左側陣列剩餘複製完
copy[indexCopy--] = data[i];
for(; j >= start + length + 1; --j)//右側陣列剩餘複製完
copy[indexCopy--] = data[j];
//左側逆序對數+右側逆序對數+本批計算得逆序對數
return left + right + count;
}
// 引數:
// data: 一個整數陣列
// length: 陣列的長度
// 返回值:
// data陣列中逆序對的總數
int InversePairs(int* data, int length) {
//空陣列或者長度不合法,這裡應用<=0而不是<0
if(data == nullptr || length <= 0)
return 0;
//深拷貝data陣列到copy陣列中
int* copy = new int[length];
for(int i = 0; i < length; ++i)
copy[i] = data[i];
//呼叫統計逆序對數目的遞迴函式
int count = InversePairsCore(data, copy, 0, length - 1);
delete[] copy;
return count;
}
int main() {
int data[] = { 1, 2, 3, 4, 7, 6, 5 };
cout<<InversePairs(data,7)<<endl;//3
return 0;
}
面試題52:兩個連結串列的第一個公共結點
輸入兩個連結串列,找出它們的第一個公共結點。
書上三種方法: ①在第一個連結串列上每遍歷到一個點就在第二個連結串列上遍歷一遍。 ②用兩個棧,兩個連結串列上邊遍歷邊壓棧,然後同步出棧。 ③先各自遍歷一遍,然後就知道兩個連結串列長度和長度差。在長的連結串列上先遍歷這麼多步,然後同步遍歷直到指標重合。
#include<bits/stdc++.h>
#include"../Utilities/List.h"
using namespace std;
//遍歷獲得連結串列長度
unsigned int GetListLength(ListNode* pHead) {
unsigned int nLength = 0;
ListNode* pNode = pHead;
while(pNode != nullptr) {
++nLength;
pNode = pNode->m_pNext;
}
return nLength;
}
//尋找兩個連結串列的第一個公共結點
ListNode* FindFirstCommonNode(ListNode *pHead1, ListNode *pHead2) {
//得到兩個連結串列的長度
unsigned int nLength1 = GetListLength(pHead1);
unsigned int nLength2 = GetListLength(pHead2);
int nLengthDif = nLength1 - nLength2;//長度差,即先行步數
//預設鏈1長,鏈2短
ListNode* pListHeadLong = pHead1;
ListNode* pListHeadShort = pHead2;
//如果鏈2長
if(nLength2 > nLength1) {
//反過來
pListHeadLong = pHead2;
pListHeadShort = pHead1;
nLengthDif = nLength2 - nLength1;
}
//先在長連結串列上走nLengthDif步
for(int i = 0; i < nLengthDif; ++i)
pListHeadLong = pListHeadLong->m_pNext;
//然後同時在兩個連結串列上遍歷:只要長短鏈都沒遍歷完,且還沒找到公共結點
while((pListHeadLong != nullptr) &&
(pListHeadShort != nullptr) &&
(pListHeadLong != pListHeadShort)) {
//同步指標向下走
pListHeadLong = pListHeadLong->m_pNext;
pListHeadShort = pListHeadShort->m_pNext;
}
//返回得到的第一個公共結點,如果沒有自然返回nullptr也沒問題
ListNode* pFisrtCommonNode = pListHeadLong;
return pFisrtCommonNode;
}
// 1 - 2 - 3 \
// 6 - 7
// 4 - 5 /
int main() {
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
ListNode* pNode6 = CreateListNode(6);
ListNode* pNode7 = CreateListNode(7);
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode6);
ConnectListNodes(pNode4, pNode5);
ConnectListNodes(pNode5, pNode6);
ConnectListNodes(pNode6, pNode7);
cout<<FindFirstCommonNode(pNode1,pNode4)->m_nValue<<endl;//6
DestroyList(pNode1);
DestroyList(pNode4);
return 0;
}