(手撕)基本算法


一、快速排序

1、數組實現

public static void quickSort(int[] arr,int low,int high){
        int i,j,temp,t;
        if(low>=high){
            return;
        }
        i=low;
        j=high;
        //temp就是基准位
        temp = arr[low];

        while (i<j) {
            //先看右邊,依次往左遞減
            while (temp<=arr[j]&&i<j) {
                j--;
            }
            //再看左邊,依次往右遞增
            while (temp>=arr[i]&&i<j) {
                i++;
            }
            //如果滿足條件則交換
            if (i<j) {
                t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
            }

        }
        //最后將基准為與i和j相等位置的數字交換
        arr[low] = arr[i];
        arr[i] = temp;
        //遞歸調用左半數組
        quickSort(arr, low, j-1);
        //遞歸調用右半數組
        quickSort(arr, j+1, high);
    }

2、鏈表實現

public class QuickSortList {
    public void quickSort(LNode head, LNode tail) {
        if(head == null || head == tail)
            return ;
        LNode pBase = head;//作為樞紐值的結點
        LNode pleft = pBase;//指向當前最后一個比樞紐值小的結點,pBase與pleft之間的結點值始終都比樞紐值小,
        LNode pright = pBase.next;//指向比樞紐值大的結點
        int temp;
        while(pright != tail) {
            //作為遍歷的pright指針,此時當pright找到了下一個比基准值小的結點,就把pleft右移,將pright的值與pleft交換
            if(pright.val < pBase.val) {
                pleft = pleft.next;//移向下一個存儲小值的位置
                if(pright != pleft) {
                    temp = pleft.val;
                    pleft.val = pright.val;
                    pright.val = temp;
                }           
            }
            pright = pright.next;
        }
        temp = pBase.val;
        pBase.val = pleft.val;
        pleft.val = temp;//原pleft的下一個結點就比樞紐值大
        
        quickSort(pBase, pleft);
        quickSort(pleft.next, tail);
    }
    
}

二、歸並排序

1、數組實現

    public static void merge(int []a,int left,int mid,int right){
        int []tmp=new int[a.length];//輔助數組
        int p1=left,p2=mid+1,k=left;//p1、p2是檢測指針,k是存放指針

        while(p1<=mid && p2<=right){
            if(a[p1]<=a[p2])
                tmp[k++]=a[p1++];
            else
                tmp[k++]=a[p2++];
        }

        while(p1<=mid) tmp[k++]=a[p1++];//如果第一個序列未檢測完,直接將后面所有元素加到合並的序列中
        while(p2<=right) tmp[k++]=a[p2++];//同上

        //復制回原素組
        for (int i = left; i <=right; i++)
            a[i]=tmp[i];
    }

    public static void mergeSort(int [] a,int start,int end){

        if(start<end){//當子序列中只有一個元素時結束遞歸
            int mid=(start+end)/2;//划分子序列
            mergeSort(a, start, mid);//對左側子序列進行遞歸排序
            mergeSort(a, mid+1, end);//對右側子序列進行遞歸排序
            merge(a, start, mid, end);//合並
        }
    }

2、鏈表實現

  • 找到中間的拆分鏈表
//找到中間點,然后分割
    public ListNode getMiddle(ListNode head) {
        if (head == null) {
            return head;
        }
        //快慢指針
        ListNode slow, fast;
        slow = fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
  • 合並排好序的兩個鏈表
// 合並排好序的鏈表
    public ListNode merge(ListNode a, ListNode b) {
        ListNode dummyHead, curr;
        dummyHead = new ListNode(0);
        curr = dummyHead;
        while (a != null && b != null) {
            if (a.val <= b.val) {
                curr.next = a;
                a = a.next;
            } else {
                curr.next = b;
                b = b.next;
            }
            curr = curr.next;
        }
        curr.next = (a == null) ? b : a;
        return dummyHead.next;
    }
  • 單鏈表的歸並
/單鏈表的歸並排序
    public ListNode merge_sort(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        //得到鏈表中間的數
        ListNode middle = getMiddle(head);
        ListNode sHalf = middle.next;
        //拆分鏈表
        middle.next = null;
        //遞歸調用
        return merge(merge_sort(head), merge_sort(sHalf));
    }

三、、堆排序

1、大根堆(用來升序)數組存儲

  1. 首先將無需數組構造成一個大根堆(新插入的數據與其父結點比較)
  2. 固定一個最大值,將剩余的數重新構造成一個大根堆,重復這樣的過程
 //堆排序
    public static void heapSort(int[] arr) {
        //構造大根堆
        heapInsert(arr);
        int size = arr.length;
        while (size > 0) {
            //固定最大值
            swap(arr, 0, size - 1);
            size--;
            //構造大根堆
            heapify(arr, 0, size);
 
        }
 
    }
 
    //構造大根堆(通過新插入的數上升)
    public static void heapInsert(int[] arr) {
        for (int i = 1; i < arr.length; i++) {
            //當前插入的索引
            int currentIndex = i;
            //父結點索引
            int fatherIndex = (currentIndex - 1) / 2;
            //如果當前插入的值大於其父結點的值,則交換值,並且將索引指向父結點
            //然后繼續和上面的父結點值比較,直到不大於父結點,則退出循環
            while (arr[currentIndex] > arr[fatherIndex]) {
                //交換當前結點與父結點的值
                swap(arr, currentIndex, fatherIndex);
                //將當前索引指向父索引
                currentIndex = fatherIndex;
                //重新計算當前索引的父索引
                fatherIndex = (currentIndex - 1) / 2;
            }
        }
    }
    //將剩余的數構造成大根堆(通過頂端的數下降)
    public static void heapify(int[] arr, int index, int size) {
        int left = 2 * index + 1;
        int right = 2 * index + 2;
        while (left < size) {
            int largestIndex;
            //判斷孩子中較大的值的索引(要確保右孩子在size范圍之內)
            if (arr[left] < arr[right] && right < size) {
                largestIndex = right;
            } else {
                largestIndex = left;
            }
            //比較父結點的值與孩子中較大的值,並確定最大值的索引
            if (arr[index] > arr[largestIndex]) {
                largestIndex = index;
            }
            //如果父結點索引是最大值的索引,那已經是大根堆了,則退出循環
            if (index == largestIndex) {
                break;
            }
            //父結點不是最大值,與孩子中較大的值交換
            swap(arr, largestIndex, index);
            //將索引指向孩子中較大的值的索引
            index = largestIndex;
            //重新計算交換之后的孩子的索引
            left = 2 * index + 1;
            right = 2 * index + 2;
        }
 
    }
    //交換數組中兩個元素的值
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

2、小根堆(用來降序)數組存儲

四、字典樹(Trie)的實現

//字典樹的java實現
    public class Trie {
        private TrieNode root;

        public Trie() {
            root = new TrieNode();
            root.wordEnd = false;
        }

        public void insert(String word) {
            TrieNode node = root;
            for (int i = 0; i < word.length(); i++) {
                Character c = new Character(word.charAt(i));
                if (!node.childdren.containsKey(c)) {
                    node.childdren.put(c, new TrieNode());
                }
                node = node.childdren.get(c);
            }
            node.wordEnd = true;
        }

        public boolean search(String word) {
            TrieNode node = root;
            boolean found = true;
            for (int i = 0; i < word.length(); i++) {
                Character c = new Character(word.charAt(i));
                if (!node.childdren.containsKey(c)) {
                    return false;
                }
                node = node.childdren.get(c);
            }
            return found && node.wordEnd;
        }

        public boolean startsWith(String prefix) {
            TrieNode node = root;
            boolean found = true;
            for (int i = 0; i < prefix.length(); i++) {
                Character c = new Character(prefix.charAt(i));
                if (!node.childdren.containsKey(c)) {
                    return false;
                }
                node = node.childdren.get(c);
            }
            return found;
        }

    }

    public class TrieNode {
        Map<Character, TrieNode> childdren;
        boolean wordEnd;

        public TrieNode() {
            childdren = new HashMap<Character, TrieNode>();
            wordEnd = false;
        }
    }

五、樹的非遞歸遍歷

1、前序

//先序遍歷非遞歸
    public static void preOrder2(TreeNode root) {
        if(root==null) return;
        ArrayList<Integer> ans=new ArrayList();
        Stack<TreeNode > stack=new Stack<>();
        while(root!=null || !stack.empty()){
            while(root!=null){
                ans.add(root.val);
                stack.push(root);
                root=root.left;
            }
            //root的左為空就去pop這個root
            if(!stack.empty()){
                root=stack.pop();//right為空就去找棧里面的頂端節點
                root=root.right;
            }
        }
    }

2、中序

//中序遍歷非遞歸
    public static void inOrder2(TreeNode root) {
        if(root==null) return;
        ArrayList<Integer> ans=new ArrayList();
        Stack<TreeNode > stack=new Stack<>();
        while(root!=null || !stack.empty()){
            while(root!=null){
                stack.push(root);
                root=root.left;
            }
            //root的左為空就去pop這個root
            if(!stack.empty()){
                root=stack.pop();
                ans.add(root.val);
                root=root.right;
            }
        }
    }

3、后序

//后續遍歷非遞歸
    public static void postOrder2(TreeNode root) {
        if(root==null) return;
        ArrayList<Integer> ans=new ArrayList();
        Stack<TreeNode > stack=new Stack<>();
        Stack<Integer> tag=new Stack<>();
        while(root!=null || !stack.empty()){
            while(root!=null){
                stack.push(root);
                tag.push(0);
                root=root.left;
            }
            //root的左為空就去pop這個root
            if(!stack.empty() &&  tag.peek()==1){//tag為1說明訪問過這個根的右節點了
                tag.pop();
                ans.add(stack.pop().val);
            }
            if(!stack.empty()){
                tag.pop();
                tag.push(1);
                root=stack.peek();//此時的根節點
                root=root.right;

            }
        }
    }

六、其他常考代碼

1、求二叉樹的最大深度?

public int getHeight(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftHeight = getHeight(root.getLeft());
        int rightHeight = getHeight(root.getRight());
        return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

2、判斷是否為平衡二叉樹?

法一:每次都要算一遍高度,不好。

public class Solution {
    public boolean IsBalanced_Solution(TreeNode root) {
        if( root == null) { //一棵空樹就是平衡二叉樹
            return true;
        }
        if( Math.abs(getDepth(root.left) - getDepth(root.right)) <= 1 ) {
            //滿足左右子樹高度差小於等於1,那就接着判斷左右子樹是不是二叉樹
            return (IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right));
        } else {
            //不滿足左右子樹高度差小於等於1,那這棵樹肯定不是平衡二叉樹啦
            return false;
        }
    }
    
    public int getDepth(TreeNode root) {
        if( root == null ) return 0;
        int left = getDepth(root.left);
        int right = getDepth(root.right);
        return ( left > right ? left : right ) + 1;//樹的高度怎么計算就不用我講了吧
    }
}

法二,從下往上走,算出高度后可以繼續使用這個高度,比較好。

public class Solution {
    private boolean isBalanced = false;//最后的返回值
    public boolean IsBalanced_Solution(TreeNode root) {
        getDepth(root);
        return isBalanced;
    }
    public int getDepth(TreeNode root) {
        if(root == null) {
            isBalanced = true;
            return 0;
        }
        int left = getDepth(root.left);//左子樹
        int right = getDepth(root.right);//右子樹
        int depth = (left > right ? left : right) + 1;
        if(Math.abs(left - right) <= 1) {
            isBalanced = true;
        } else {
            isBalanced = false;
        }
        return depth;//下層的深度,上層可以接着用免得再遍歷
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM