資料結構與演算法-緒論
阿新 • • 發佈:2020-07-24
緒論
演算法:即是在特定計算模型下,旨在解決特定問題的指令序列
要保證正確性、確定性、可行性、有窮性
有窮性:
例子1:HailStone序列
@Test public void test1() { int n = 7; int length = 1; while (n > 1) { n = ((n % 2) > 0) ? 3 * n + 1 : n / 2; length++; } System.out.println(length); }
層級級別:
例子2:計算任意N個整數之和
減而治之
@Test public void test4() { int[] A = {100, 836, 3236, 5, 16, 26, -3, 89, 69, 43}; long begintime = System.nanoTime(); /*int n = A.length;//簡單遞迴 System.out.println(sum1(A,n));*/ int lo = 0;//二分遞迴 int hi = A.length - 1; System.out.println(sum2(A, lo, hi)); long endtime = System.nanoTime(); long costTime = (endtime - begintime); System.out.println(costTime); } public int sum1(int A[], int n) { return (n < 1) ? 0 : sum1(A, n - 1) + A[n - 1]; }
分而治之
public int sum2(int A[], int lo, int hi) {
if (lo == hi) {
return A[lo];
}
int mi = (lo + hi) >> 1;
return sum2(A, lo, mi) + sum2(A, mi + 1, hi);
}
陣列反向
@Test public void test5() { int[] A = {100, 836, 3236, 5, 16, 26, -3, 89, 69, 43}; int lo = 0; int hi = A.length - 1; reverse(A, lo, hi); } public void reverse(int[] A, int lo, int hi) { if (lo < hi) { int a = A[lo]; A[lo] = A[hi]; A[hi] = a; System.out.println(Arrays.toString(A)); reverse(A, lo + 1, hi - 1); } }
例子3:從陣列區間中找出最大的兩個整數元素
@Test
public void test6() {
int[] A = {100, 836, 3236, 5, 16, 26, -3, 89, 69, 43};
int lo = 0;
int hi = A.length - 1;
/*int[] int1 = max2(A, lo, hi);*/
/*int[] int2 = max02(A, lo, hi);*/
int[] int3 = max002(A, lo, hi);
System.out.println(Arrays.toString(int3));
}
public int[] max2(int[] A, int lo, int hi) {
int x1 = lo;
int x2 = lo;
//掃描lo-hi,找到x1
for (int i = lo + 1; i < hi; i++) {
if (A[x1] < A[i]) {
x1 = i;
}
}
//掃描lo-x1,找到較大值
for (int i = lo + 1; i < x1; i++) {
if (A[x2] < A[i]) {
x2 = i;
}
}
//掃描x1-hi,找到x2
for (int i = x1 + 1; i < hi; i++) {
if (A[x2] < A[i]) {
x2 = i;
}
}
return new int[]{x1, x2};
}
改進1:
public int[] max02(int[] A, int lo, int hi) {
int x1 = lo;
int x2 = lo + 1;
if (A[x1] < A[x2]){
int a = A[x1];
A[x1] = A[x2];
A[x2] = a;
}
for (int i = lo + 2;i < hi;i++){
if (A[x2] < A[i]){
if (A[x1] < A[x2 = i]){
int a = A[x1];
A[x1] = A[x2];
A[x2] = a;
}
}
}
return new int[]{x1, x2};
}
改進2:
public int[] max002(int[] A, int lo, int hi) {
int x1 = 0;
int x2 = 0;
if (lo + 1 == hi){
if (A[x1 = lo] < A[x2 = hi]){
int a = x1;
x1 = x2;
x2 = a;
}
return new int[]{x1,x2};
}
if (lo + 2 == hi){
int mid = (lo + hi)/2;
if ((A[lo] > A[mid]) && (A[lo] > A[hi])){
x1 = lo;
x2 = (A[mid] > A[hi]) ? mid:hi;
}else if((A[lo] < A[mid]) && (A[mid] > A[hi])){
x1 = mid;
x2 = (A[lo] > A[hi]) ? lo:hi;
}else{
x1 = hi;
x2 = (A[lo] > A[mid]) ? lo:mid;
}
return new int[]{x1,x2};
}
int mi = (lo + hi) / 2;
int[] L= max002(A, lo, mi);
int[] R = max002(A, mi+1, hi);
if (A[L[0]] > A[R[0]]){
x1 = L[0];
x2 = (A[L[1]] > A[R[0]]) ? L[1]:R[0];
}else{
x1 = R[0];
x2 = (A[L[0]] > A[R[1]]) ? L[0]:R[1];
}
return new int[]{x1,x2};
}
例子3.5:最大綜合區間(還沒寫)
蠻力
遞增
分治
減治
例子4:斐波那契數列
@Test
public void test7(){
for (int i = 0;i < 25;i++){
System.out.println(fib(i));
}
}
public int fib(int n){
int f = 0;
int g = 1;
while(0 < n--){
g = g + f;
f = g - f;
}
return g;
}
例子5:最長公共子序列
情況分析
@Test
public void test8(){
char[] x = {'A','B','C','B','D','A','B'};
char[] y = {'B','D','C','A','B','A'};
int[][] b = new int[x.length+1][y.length+1];
int[][] c = lcsLength(x,y,b);
System.out.println(c[x.length][y.length]);
lcs(x.length,y.length,x,b);
}
//從[0][0]向[x.length+1][y.length+1]不斷的得到1、共同序列個數 2、各種情況並作出標記
public int[][] lcsLength(char[] x,char[] y,int[][] b) {
//給第一行,第一列設定空序列
int[][] c = new int[x.length+1][y.length+1]; //0存空序列
for(int i=0;i<c.length;i++){
for(int j=0;j<c[0].length;j++){
c[i][j] = 0;
}
}
//進行規劃
for (int i = 1;i <= x.length;i++){
for (int j = 1;j <= y.length;j++){
//情況一、末位相等,去除最後一個值並比較前面的(減而治之)
if (x[i-1] == y[j-1]){
c[i][j] = c[i-1][j-1]+1;
b[i][j] = 1;
}else{
//情況二、不相等,分別去除其中一個數列的末位並進行比較(分而治之)
if(c[i-1][j]>=c[i][j-1]){
c[i][j] = c[i-1][j];
b[i][j] = 2;
}else{
c[i][j] = c[i][j-1];
b[i][j] = 3;
}
}
}
}
//返回規劃好的情況c
return c;
}
public void lcs(int i,int j,char[]x,int[][]b){
//結束條件
if (i==0 || j==0){
return;
}
//判斷b[i][j]進入不同分支
if (b[i][j]==1){
//減而治之
lcs(i-1,j-1,x,b);
System.out.print(x[i-1]);
}else {
//分而治之
if (b[i][j] == 2){
lcs(i-1,j,x,b);
}else{
lcs(i,j-1,x,b);
}
}
}
空間優化
/**
* 方法二、進行空間上的優化
* 通過狀態方程可知,計算c[i][j]時只需知道c[i-1][j-1]、c[i-1][j]、c[i][j-1]就行了
* 那麼就和斐波那契數列比較相似,可以利用滾動陣列
*/
//從[0][0]向[x.length+1][y.length+1]不斷的得到1、共同序列個數 2、各種情況並作出標記
public Set<Character> lcsLength2(char[] x, char[] y) {
Set<Character> set = new HashSet<>();
//給第一行,第一列設定空序列
int[][] c = new int[x.length+1][y.length+1]; //0存空序列
for(int i=0;i<c.length;i++){
for(int j=0;j<c[0].length;j++){
c[i][j] = 0;
}
}
//進行規劃
for (int i = 1;i <= x.length;i++){
for (int j = 1;j <= y.length;j++){
//情況一、末位相等,去除最後一個值並比較前面的(減而治之)
if (x[i-1] == y[j-1]){
c[i%2][j] = c[(i-1)%2][j-1]+1;
char a = y[j-1];
set.add(a);
}else{
//情況二、不相等,分別去除其中一個數列的末位並進行比較(分而治之)
c[i%2][j] = Math.max(c[i%2][j-1],c[(i-1)%2][j]);
}
}
}
//返回規劃好的情況c
return set;
}
遞迴版本(沒寫JAVA版的)
function LCS(str1, str2, a, b) {
if(a === void 0){
a = str1.length - 1
}
if(b === void 0){
b = str2.length - 1
}
if(a == -1 || b == -1){
return 0
}
if(str1[a] == str2[b]) {
return LCS(str1, str2, a-1, b-1)+1;
}
if(str1[a] != str2[b]) {
var x = LCS(str1, str2, a, b-1)
var y = LCS(str1, str2, a-1, b)
return x >= y ? x : y
}
}