目錄
一、快速排序
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、大根堆(用來升序)數組存儲
- 首先將無需數組構造成一個大根堆(新插入的數據與其父結點比較)
- 固定一個最大值,將剩余的數重新構造成一個大根堆,重復這樣的過程
//堆排序
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;//下層的深度,上層可以接着用免得再遍歷
}
}