二分法與二叉樹的 Java 實現

      

算法與數據結構始終是計算機基礎的重要一環,今天咱們來討論下 Java 中二叉樹的實現以及一些簡單的小算法,如二分查找,歸併排序等。java

   

  • 二分查找git

 

二分查找是一種在有序數組中查找某一特定元素的搜索算法,它在開發中應用的也是很是普遍,須要注意的是二分法是創建在有序數組基礎上的快速查找,因此通常須要先對數組進行排序。github

 

  • 算法思想算法

     

  1. 搜素過程從數組的中間元素開始,若是中間元素正好是要查找的元素,則搜素過程結束;數組

  2. 若是某一特定元素大於或者小於中間元素,則在數組大於或小於中間元素的那一半中查找,並且跟開始同樣從中間元素開始比較;數據結構

  3. 若是在某一步驟數組爲空,則表明找不到,若是找到則返回post

 

這種搜索算法的特色是每一次比較都使搜索範圍縮小一半。測試

 

  • 實現思路ui

 

  1. 找出位於數組中間的值,並存放在一個變量中(爲了下面的說明,變量暫時命名爲 base 基準值);this

  2. 須要找到的 key 和 base 進行比較;

  3. 若是 key 值大於 base,則把數組中間位置做爲下一次計算的起點;重複 1 2;

  4. 若是 key 值小於 base,則把數組中間位置做爲下一次計算的終點;重複 1 2; 

  5. 若是 key 值等於 base,則返回數組下標,完成查找。

 

  • 代碼以下:

/** * 二分法查找 : 在有序數組中查找特定元素的算法。(有序數組) */public class TwoSplitSearch { public static void main(String[] args) {        int[] data = {-10, -3, 1, 4, 6, 8, 10, 22, 33, 44, 100, 203}; //存在 System.out.println(towSplitSearch(data, 0, data.length - 1,4)); System.out.println(towSplitSearch(data, 0, data.length - 1,9)); } /** * 二分查找 * * @param data 有序數組 * @param from 開始下標 * @param to 終止下標 * @param key 目標值 * @return int 目標值的下標,若是沒有返回 -1 */    private static int towSplitSearch(int[] data, int from, int to, int key) { if (from < 0 || to < 0) { return -1; } // 判斷 from 不能大於 to if(from <= to ){ //獲取數組中間下標 int centerIndex = (from + to) / 2; //將數組中間值做爲基準值比較 int base = data[centerIndex]; //目標值比基準值大,則須要往中間下標後查找,起始點爲 centerIndex + 1 if (key > base) { from = centerIndex + 1; //目標值比基準值小,則須要往中間下標1前查找,終止點爲 centerIndex - 1 } else if (key < base) { to = centerIndex - 1; } else { return centerIndex; }        }else{ return -1;        }        return towSplitSearch(data, from, to, key);    }}



在瞭解二分法以後,還須要知道一些簡單的排序算法,下面咱們介紹下快速排序法和歸併排序法。

 

  • 快速排序

 

快速排序是經過在數組中選定基準值,經過其餘元素與基準值的比較,使得基準值前是比基準值小的數組,基準值後是比基準值大的數組,再經過遞歸調用完成排序的方法。

 

  • 算法思想

 

  1. 經過一趟排序將要排序的數據分割成獨立的兩部分;

  2. 其中一部分的全部數據都比另一部分的全部數據都要小,基準值在中間;

  3. 再按此方法對這兩部分數據分別進行快速排序;

  4. 整個排序過程能夠遞歸進行,以此達到整個數據變成有序序列。

 

 

  • 實現思路

     

  1. 選取基準值,通常選擇數組第一個元素

  2. 從後往前與基準值比較,若是比基準值小,與其調換位置

  3. 再從前日後比較,若是比基準值大,與其調換位置

  4. 通過比較後,達到基準值前元素都比它小,基準值後元素都比它大

  5. 遞歸比較,將基準值前全部元素看作一個新數組比較,後全部元素看作一個新數組比較

 

  • 代碼以下:

    

public class QuickSort { public static void main(String[] args){ int[] a = {1,5,45,-2,-44,3,20,8,11,-7}; System.out.println(Arrays.toString(a)); quickSort(a); System.out.println(Arrays.toString(a)); } private static void quickSort(int[] data) { if(data.length > 0){ quickSubSort(data,0,data.length-1); } } /** * * @param data * @param low 最小下標 * @param high 最高小標 */ private static void quickSubSort(int[] data, int low, int high) { // 定義基準值 int base = data[low]; //定義開始下標 int start = low; //定義結束下標 int end = high; while(end > start){ //從後往前找,找到比基準值大的 放過,讓下標減1 ,直到找到比base小的下標 end while(end > start && data[end] >= base){ end --; } // 找到比base小的下標,與base交換位置 if(end > start && data[end] < base){ swap(data,start,end); } //開始從前日後找,找到比基準值小的放過,讓start下標加1,直到找到比base大的下標 end while(end > start && data[start] <= base){ start ++; } // 找到比base大的下標,與base交換位置 if(end > start && data[start] > base){ swap(data,start,end); } } //第一次循環後 開始遞歸調用 //基準值前的數 if(start > low){ quickSubSort(data,low,start-1); } //基準值後的數 if(end < high){ quickSubSort(data,end+1,high);        } } /** * 交換位置 * @param data * @param start * @param end */    private static void swap(int[] data, int start, int end) { int temp = data[start]; data[start] = data[end];        data[end] = temp;    }}

 

  • 歸併排序

 

歸併排序 是創建在歸併操做上的一種有效的排序算法,該算法是採用分治法的一個很是典型的應用。 

 

  • 算法思想

 

  1. 將一個序列拆分紅兩個序列;

  2. 先使每一個子序列有序,再使子序列段間有序; 

  3. 將已有序的子序列合併,獲得徹底有序的序列。

 

  • 實現思路

     

  1. 使用二路歸併法,將兩個有序表合併成一個有序表;

  2. 新建臨時數組,用於存儲比較後的數值;

  3. 將左右數組同時比較,小的放入臨時數組中;

  4. 一旦有某一數組比較完成,則將剩下的數組全放到臨時數組中;

  5. 最後將臨時數組複製回原數組。

 

  • 代碼以下:

 

/** * 歸併排序,二分歸併 * 新建臨時數組,將左右數組同時比較,小的放入臨時數組中 * 一旦有某一數組比較完成,則將剩下的數組全放到臨時數組中 * 兩個循環只會走一個,由於只有一個數組沒有遍歷完 * <p> * 最後將臨時數組複製回原數組 */public class MergeSort { public static void main(String[] args) { int[] a = {1, 5, 45, -2, -44, 3, 20, 8, 11, -7}; System.out.println(Arrays.toString(a)); mergeSort(a); System.out.println(Arrays.toString(a)); } private static void mergeSort(int[] a) { if (a.length > 0) { mergeSubSort(a, 0, a.length - 1); } } /** * 二分歸併 * * @param data * @param left * @param right */ private static void mergeSubSort(int[] data, int left, int right) { if (left >= right) { return; } //獲取中間值 int center = (left + right) / 2; //劃分紅左右2個數組 mergeSubSort(data, left, center); mergeSubSort(data, center + 1, right); //開始歸併排序 merge(data, left, center, center + 1, right); } /** * @param data * @param leftStart * @param leftEnd * @param rightStart * @param rightEnd */ private static void merge(int[] data, int leftStart, int leftEnd, int rightStart, int rightEnd) { //定義循環開始左下標 int leftIndex = leftStart; //定義循環開始右下標 int rightIndex = rightStart; //定義臨時數組開始下標 int tempIndex = 0; //定義臨時數組 int[] temp = new int[rightEnd - leftStart + 1]; //開始循環 ,當左右有任意一方下標達到臨界值 中止循環 while (leftIndex <= leftEnd && rightIndex <= rightEnd) { //比較最小值,將最小值放到臨時數組中 if (data[leftIndex] > data[rightIndex]) { temp[tempIndex++] = data[rightIndex++]; } else { temp[tempIndex++] = data[leftIndex++]; } } //有一方數組循環完成 // 一下循環只有其實只有一個執行 while (leftIndex <= leftEnd) { temp[tempIndex++] = data[leftIndex++]; } while (rightIndex <= rightEnd) { temp[tempIndex++] = data[rightIndex++]; } //將臨時數組複製回原數組 tempIndex = leftStart; for (int element : temp) { data[tempIndex++] = element; } }}

 

  • 二叉樹

 

二叉樹是一種很是重要的數據結構,它同時具備數組和鏈表各自的特色:它能夠像數組同樣快速查找,也能夠像鏈表同樣快速添加。可是它也有本身的缺點:刪除操做複雜。查找,插入,刪除的複雜度都爲 O(logN)。

 

二叉查找樹:是每一個結點最多有兩個子樹的有序樹,在使用二叉樹的時候,數據並非隨便插入到節點中的,一個節點的左子節點的關鍵值必須小於此節點,右子節點的關鍵值必須大於或者是等於此節點,因此又稱二叉排序樹、二叉搜索樹。

 

下面咱們就以 int 類型做爲樹的根節點,來看看 二叉樹的 Java 實現。

 

  • 定義節點

 

/** * 定義節點類型 * 主要屬性: value 值 left 左節點 right 右節點 * */public class TreeNode { //關鍵值 private int value; //左子樹 private TreeNode left; //右子樹 private TreeNode right; //刪除狀態 private Boolean deleteStatus; public TreeNode() { } public TreeNode(int value) { this(value,null,null,false); } public TreeNode(int value, TreeNode left, TreeNode right, Boolean deleteStatus) { this.value = value; this.left = left; this.left = right; this.deleteStatus = deleteStatus; }…… get set 方法}

 

  • 定義二叉樹

 

利用節點來建立二叉樹,因爲二叉樹刪除操做比較複雜,這裏使用刪除標識 deleteStatus 來記錄刪除狀態。代碼中主要寫的是樹的插入,查找,遍歷。

 

/** * 建立 樹 * 主要屬性 root 只有獲取根節點方法 * * 主要方法 * 插入 * 查找 * 遍歷 * */public class BinaryTree { private TreeNode root; public TreeNode getRoot() { return root; } /** * 向樹中插入數據 * @param value */ public void insert(int value){ TreeNode newNode = new TreeNode(value); //插入數據時判斷是不是根節點插入 if(root == null){ root = newNode; root.setLeft(null); root.setRight(null); }else{ // 不是根節點插入,獲取根節點做爲當前節點 TreeNode currentNode = root; TreeNode parentNode; //循環插入,直到找到葉子節點,將新值插入 while(true){ //將根節點賦值給父節點 parentNode = currentNode; if(newNode.getValue() > currentNode.getValue()) { currentNode = currentNode.getRight(); if (currentNode == null) { parentNode.setRight(newNode); return; } }else{ currentNode = currentNode.getLeft(); if(currentNode == null){ parentNode.setLeft(newNode); return; } } } } } /** * 查找 * * @param value * @return */ public TreeNode find(int value){ //獲取根節點做爲當前節點 TreeNode currentNode = root; //根節點不爲null if(root != null){ while (currentNode.getValue() != value){ if(value > currentNode.getValue()){ currentNode = currentNode.getRight(); }else{ currentNode = currentNode.getLeft(); } if(currentNode == null){ return null; } } if(currentNode.getDeleteStatus()){ return null; }else{ return currentNode; }        }else{            return null;        }    } /** * 中序遍歷 * @return */    public void inOrder(TreeNode root){ if(root != null){ inOrder(root.getLeft()); System.out.println(root.getValue());            inOrder(root.getRight()); }    } /** * 前序遍歷 * @return */    public void preOrder(TreeNode root){ if(root != null){ System.out.println(root.getValue()); preOrder(root.getLeft());            preOrder(root.getRight());        } } /** * 後序遍歷 * @return */    public void postOrder(TreeNode root){ if(root != null){ postOrder(root.getLeft()); postOrder(root.getRight());            System.out.println(root.getValue());        }    }}

 

  • 測試代碼

 

public static void main(String[] args){ BinaryTree binaryTree = new BinaryTree(); binaryTree.insert(10); binaryTree.insert(3); binaryTree.insert(5); binaryTree.insert(20); binaryTree.insert(30); binaryTree.insert(15); binaryTree.insert(45); binaryTree.insert(123); TreeNode root = binaryTree.getRoot(); System.out.println("跟節點是"+root.getValue()); TreeNode treeNode = binaryTree.find(5); if(treeNode != null){ System.out.println("找到了"); }else{ System.out.println("沒找到"); } System.out.println("===前序===="); binaryTree.preOrder(root); System.out.println("===中序===="); binaryTree.inOrder(root); System.out.println("===後序===="); binaryTree.postOrder(root);}

 

以上即是二叉樹的 Java 實現,相關代碼參照參考資料。

 

參考資料:

 

https://github.com/fanpengyi/jianzhi-offer   ----文中代碼地址

 

 

 

關注一下,我寫的就更來勁兒啦 

 

相關文章
相關標籤/搜索