數(shù)據(jù)結(jié)構(gòu)算法總結(jié)

冒泡排序

原理:比較兩個(gè)相鄰的元素,將值大的元素交換至右端集灌。

思路:依次比較相鄰的兩個(gè)數(shù),將小數(shù)放在前面,大數(shù)放在后面肺樟。即在第一趟:首先比較第1個(gè)和第2個(gè)數(shù)凫佛,將小數(shù)放前芙扎,大數(shù)放后负乡。然后比較第2個(gè)數(shù)和第3個(gè)數(shù)榜田,將小數(shù)放前,大數(shù)放后锻梳,如此繼續(xù)箭券,直至比較最后兩個(gè)數(shù),將小數(shù)放前疑枯,大數(shù)放后辩块。重復(fù)第一趟步驟,直至全部排序完成荆永。

        for (int e = arr.length - 1; e > 0; e--) {
            for (int i = 0; i < e; i++) {
                if (arr[i] > arr[i + 1]) {
                    swap(arr, i, i + 1);
                }
            }
        }

使用python實(shí)現(xiàn)废亭,每一趟的長度都應(yīng)該減少1,第一趟時(shí)要對(duì)比所有的數(shù)據(jù)具钥,從arr[0]到arr[-1]進(jìn)行對(duì)比豆村,最后一次的arr[j+1]就是arr[-1],以后依次減少對(duì)比的長度。

for i in range(len(arr))[::-1]:
    for j in range(i):
        if arr[j] > arr[j+1]:
            arr[j], arr[j+1] = arr[j + 1], arr[j]

插入排序

直接插入排序(Straight Insertion Sort)的基本思想是:把n個(gè)待排序的元素看成為一個(gè)有序表和一個(gè)無序表骂删。開始時(shí)有序表中只包含1個(gè)元素掌动,無序表中包含有n-1個(gè)元素,排序過程中每次從無序表中取出第一個(gè)元素宁玫,將它插入到有序表中的適當(dāng)位置粗恢,使之成為新的有序表,重復(fù)n-1次可完成排序過程撬统。
從后面的無序表中拿出一個(gè)數(shù),先與最后有序表最后一個(gè)元素進(jìn)行對(duì)比敦迄,如果arr[j] > arr[j + 1]恋追,就進(jìn)行插入,否則不進(jìn)行操作罚屋。

        for (int i = 1; i < arr.length; i++) {
            for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
                swap(arr, j, j + 1);
            }
        }

python實(shí)現(xiàn)苦囱,每次有序長度加1或者說每次都需要從無序列表中拿一個(gè)數(shù)據(jù),進(jìn)行插入對(duì)比是在有序里面進(jìn)行的脾猛,第一次開始的時(shí)arr[j+1]就是最新加入的數(shù)據(jù)

for i in range(1, len(arr)):
    for j in range( i-1)[::-1]:
        if arr[j] > arr[j+1]:
            arr[j] , arr[j+1] = arr[j+1] , arr[j]

選擇排序

原理:每一趟從待排序的記錄中選出最小的元素撕彤,順序放在已排好序的序列最后,直到全部記錄排序完畢猛拴。
給定數(shù)組:int[] arr={里面n個(gè)數(shù)據(jù)}羹铅;第1趟排序,在待排序數(shù)據(jù)arr[1]~ arr[n]中選出最小的數(shù)據(jù)愉昆,將它與arrr[1]交換职员;第2趟,在待排序數(shù)據(jù)arr[2]~ arr[n]中選出最小的數(shù)據(jù)跛溉,將它與r[2]交換焊切;以此類推扮授,第i趟在待排序數(shù)據(jù)arr[i]~ arr[n]中選出最小的數(shù)據(jù),將它與r[i]交換专肪,直到全部排序完成刹勃。

        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < arr.length; j++) {
                minIndex = arr[j] < arr[minIndex] ? j : minIndex;
            }
            swap(arr, i, minIndex);
        }

二分法使用遞歸查找最大值

使用堆的壓棧和出棧理解遞歸函數(shù),每次調(diào)用一次自己嚎尤,就會(huì)把一個(gè)getmax(arr, L, R)的信息壓入棧荔仁,這時(shí)會(huì)有多個(gè)getmax(arr, L, R)函數(shù)被壓入棧,每個(gè)getmax(arr, L, R)有自己的獨(dú)立信息诺苹,當(dāng)L == R時(shí)咕晋,就會(huì)執(zhí)行出棧操作,從上往下進(jìn)行出棧操作收奔。
getmax:是通過二分法取得左側(cè)和右側(cè)的最大值掌呜,之后進(jìn)最大值之間的比較。

getmax(int [] arr, int L, int R){
    if (L == R){
    return arr[L];
  }
    int mid = (L + R) / 2;
    int maxLeft = getmax(arr, L, mid);
    int maxRight = getmax(arr, mid, R);
    return Math.max(maxLeft, maxRight)
}

并歸排序

使用遞歸進(jìn)行二分法進(jìn)行排序坪哄,把左側(cè)和右側(cè)單獨(dú)排序质蕉,之后通過外派進(jìn)行進(jìn)行排序。外排就是使用兩個(gè)指針翩肌,新建一個(gè)新數(shù)組模暗,一個(gè)指向左側(cè)第一個(gè),一個(gè)指向右側(cè)第一個(gè)念祭,把兩個(gè)指針的小值填入新數(shù)組兑宇,填入的指針加一。
解釋:void mergeSort(int[] arr, int l, int r)就是二分法的遞歸函數(shù)把左側(cè)和右側(cè)進(jìn)行排序
使用merge(int[] arr, int l, int m, int r)進(jìn)行外派合并粱坤,每次壓棧只對(duì)arr的[l, r]進(jìn)行操作隶糕,其他地方不變。

public static void mergeSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        mergeSort(arr, 0, arr.length - 1);
    }

    public static void mergeSort(int[] arr, int l, int r) {
        if (l == r) {
            return;
        }
        int mid = l + ((r - l) >> 1);
        mergeSort(arr, l, mid);
        mergeSort(arr, mid + 1, r);
        merge(arr, l, mid, r);
    }

    public static void merge(int[] arr, int l, int m, int r) {
        int[] help = new int[r - l + 1];
        int i = 0;
        int p1 = l;
        int p2 = m + 1;
        while (p1 <= m && p2 <= r) {
            help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
        }
        while (p1 <= m) {
            help[i++] = arr[p1++];
        }
        while (p2 <= r) {
            help[i++] = arr[p2++];
        }
        for (i = 0; i < help.length; i++) {
            arr[l + i] = help[i];
        }
    }

荷蘭國旗問題

輸入一個(gè)數(shù)組arr和一個(gè)數(shù)字p站玄,把小于p的數(shù)放左邊枚驻,大于p的數(shù)據(jù)放右邊,相等的放中間株旷。

思路:設(shè)置一個(gè)less指針和more指針再登。less指向arr[0],more指向arr[-1],l從0-length滑動(dòng)晾剖。如果arr[l] < p就 交換arr[less+1]和arr[l], 之后i = i +1锉矢,如果arr[l] > p就交換arr[more-1]和arr[l],重新判斷i位置,相等時(shí)直接 i = i +1.
less + 1, more - 1 是arr[l] == p區(qū)間的位置

public static int[] partition(int[] arr, int l, int r, int p) {
        int less = l - 1;
        int more = r + 1;
        while (l < more) {
            if (arr[l] < p) {
                swap(arr, ++less, l++);
            } else if (arr[l] > p) {
                swap(arr, --more, l);
            } else {
                l++;
            }
        }
        return new int[] { less + 1, more - 1 };
    }

int[] res = partition(test, 0, test.length - 1, 1);

python 實(shí)現(xiàn)荷蘭國旗問題齿尽,這里與四個(gè)指針沈撞,left,right時(shí)原始的數(shù)組開始和結(jié)尾指針雕什,less和more是在arr中小于和大于num的邊界缠俺,less和more中間的值就是等于num的值显晶。在排序過程中,less左右邊的值必為小于num的值壹士,在more右的值必為大于num的值磷雇,指針left每次都加1,但如果遇到大于num時(shí)躏救,會(huì)把當(dāng)前的值和一個(gè)未知大小的值arr[more-1]進(jìn)行交換唯笙,所以需要重新判斷一次arr[left]。

arr = [1,4,1,5,3,2,3,6]
def partition(arr, left, right, num):
    less = left - 1
    more = right + 1
    while (left < more):
        if arr[left] < num:
            less += 1
            arr[less],arr[left] = arr[left],arr[less]
            left += 1
        elif arr[left] >  num:
            more -= 1
            arr[more],arr[left] = arr[left],arr[more]
        else:
            left += 1
    print(arr)
    return less + 1, more - 1

partition(arr,0, len(arr) - 1, 3)

快排

使用遞歸劃分子集盒使,給定一個(gè)arr崩掘,首先arr取中的最后一個(gè)數(shù)arr[-1]。把a(bǔ)rr[l] < arr[-1]的數(shù)放左邊少办,arr[l] > arr[-1]的數(shù)放右邊苞慢,arr[l] == arr[-1]的數(shù)放中間。得到arr[l] == arr[-1]的邊界p英妓,p[0] - 1就是arr[l] < arr[-1]的數(shù)表挽放,p[1] + 1就是arr[l] > arr[-1]的數(shù)表,繼續(xù)進(jìn)遞歸,中間arr[l] == arr[-1]的不需要?jiǎng)勇溃f歸操作的是l-r辑畦, 遞歸返回的條件就是arr[-1]就是整數(shù)組的最大值,這時(shí)l和p[0]相等腿倚,p[1]和r相等纯出,或者p[0]==p[1]相等。
隨機(jī)快排敷燎,由于數(shù)組本身就是有序數(shù)據(jù)暂筝,這時(shí)復(fù)雜度為最大,隨機(jī)選取一個(gè)數(shù)放在arr[-1]懈叹,就可以以隨機(jī)概率在大概率情況下取得較小復(fù)雜度乖杠。

public static void quickSort(int[] arr, int l, int r) {
        if (l < r) {
            //swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
            int[] p = partition(arr, l, r);
            quickSort(arr, l, p[0] - 1);
            quickSort(arr, p[1] + 1, r);
        }
    }

    public static int[] partition(int[] arr, int l, int r) {
        int less = l - 1;
        int more = r;
        while (l < more) {
            if (arr[l] < arr[r]) {
                swap(arr, ++less, l++);
            } else if (arr[l] > arr[r]) {
                swap(arr, --more, l);
            } else {
                l++;
            }
        }
        swap(arr, more, r); // 由于最后一個(gè)數(shù)不參與劃分直接分扎,需要把r放入等于區(qū)間
        return new int[] { less + 1, more };
    }

通過把每個(gè)子數(shù)組的進(jìn)行二分澄成,把小于arr[-1]放左邊,把大于arr[-1]的放右邊,當(dāng)數(shù)組中被切分的[left, right]最大值被放到最左邊時(shí),就可進(jìn)行返回了畏吓。

import random

arr = [1,4,1,6,3,2,3,5,1]

def partition(arr, left, right):
    less = left - 1
    more = right
    num = arr[right]
    while (left < more):
        if arr[left] < num:
            less += 1
            arr[less],arr[left] = arr[left],arr[less]
            left += 1
        elif arr[left] >  num:
            more -= 1
            arr[more],arr[left] = arr[left],arr[more]
        else:
            left += 1
    arr[more],arr[right] = arr[right],arr[more]
    return arr,[less + 1, more]

def quiksort(arr, left, right):
    if left < right:
        rand = left +  int(random.random() *(right - left  + 1))
        arr[rand],arr[right] = arr[right],arr[rand]
        arr,equl = partition(arr,left, right, )
        arr = quiksort(arr, left, equl[0]-1)
        arr = quiksort(arr, equl[1]+1, right)
    return arr

print(quiksort(arr,0, len(arr) - 1))

堆結(jié)構(gòu)

  • 把一個(gè)數(shù)組建一個(gè)大根堆結(jié)構(gòu)墨状。
    i 左節(jié)點(diǎn):2 * i + 1
    i 右節(jié)點(diǎn): 2 * i + 1
    父結(jié)構(gòu): (i - 1) / 2
    所有節(jié)點(diǎn)都不能越界。
    完全二叉樹:先補(bǔ)左節(jié)點(diǎn)菲饼,再補(bǔ)右節(jié)點(diǎn)
    大根堆:完全二叉樹肾砂,任何一個(gè)子樹的最大值為子樹頭部。
    小根堆:完全二叉樹宏悦,任何一個(gè)子樹的最小值為子樹頭部镐确。

  • 對(duì)數(shù)組arr包吝,每次添加一個(gè)數(shù)到堆里。

heapInsert(int[] arr, int index) 第index個(gè)數(shù)加到堆的操作:找到index的父節(jié)點(diǎn)源葫,對(duì)比其中的最大值诗越,如果arr[index] > arr[(index - 1) / 2],第index個(gè)數(shù)比父節(jié)點(diǎn)大息堂,進(jìn)行交換嚷狞,之后與父父節(jié)點(diǎn)對(duì)比,直到index=0或者arr[index] <= arr[(index - 1) / 2].

    for (int i = 0; i < arr.length; i++) {
            heapInsert(arr, i);
        }
    public static void heapInsert(int[] arr, int index) {
        while (arr[index] > arr[(index - 1) / 2]) {
            swap(arr, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }
  • 如果堆的值發(fā)生改變:
    需要改變堆的結(jié)構(gòu)荣堰,把小值進(jìn)行下沉床未。
    在一個(gè)數(shù)組中index的值發(fā)生改變,arr中size為堆的長度振坚。
    找到index的兩個(gè)子節(jié)點(diǎn)中最大值largest薇搁,比較largest和index的小,如果largest <= index返回屡拨,否則交換swap(arr, largest, index)只酥,進(jìn)入下一個(gè)子節(jié)點(diǎn)的比較
public static void heapify(int[] arr, int index, int size) {
        int left = index * 2 + 1;
        while (left < size) {
            int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
            largest = arr[largest] > arr[index] ? largest : index;
            if (largest == index) {
                break;
            }
            swap(arr, largest, index);
            index = largest;
            left = index * 2 + 1;
        }
    }
  • 怎么進(jìn)行減堆操作:
    1.刪掉最后值,2.彈出第一個(gè)值呀狼,把最后一個(gè)值放到第一個(gè)值裂允,調(diào)整堆結(jié)構(gòu)

對(duì)一個(gè)動(dòng)態(tài)的數(shù)組建立一個(gè)堆,注意:數(shù)組的長度不固定哥艇,通過添加值的同時(shí)就可以保持?jǐn)?shù)據(jù)的有序性绝编。

通過建立一個(gè)大根堆和一個(gè)小根堆,兩個(gè)堆列的size不能超過1貌踏,如果某一個(gè)num <= big[0]進(jìn)行入大根堆十饥,否則進(jìn)入小根堆,size相差超過1,彈出頭部祖乳,這時(shí)大根堆的頭部為中位數(shù)逗堵。

堆排序

基于大根堆進(jìn)行排序,因?yàn)槊總€(gè)大根堆的頭部最大值是整個(gè)堆的最大值眷昆,通過把頭部與尾部進(jìn)行交換蜒秤,size-1,進(jìn)行對(duì)調(diào)整亚斋,循環(huán)調(diào)整作媚。

首先建堆,把數(shù)組arr堆成堆a(bǔ)rr帅刊,通過交換頭部與尾部纸泡,再調(diào)整堆結(jié)構(gòu)。

        for (int i = 0; i < arr.length; i++) {
            heapInsert(arr, i);
        }
        int size = arr.length;
        swap(arr, 0, --size);
        while (size > 0) {
            heapify(arr, 0, size);
            swap(arr, 0, --size);
        }

棧結(jié)構(gòu)

  • 先入后出的一種數(shù)據(jù)結(jié)構(gòu)赖瞒,有push女揭,pop蚤假,peek操作。
    push:往棧結(jié)構(gòu)中加入數(shù)據(jù)
    pop:從棧結(jié)構(gòu)中彈出數(shù)據(jù)吧兔,只彈出最新的數(shù)據(jù),彈出的數(shù)據(jù)不屬于棧結(jié)構(gòu)
    peek:返回棧的最后一個(gè)數(shù)據(jù)勤哗,并不刪除數(shù)據(jù)
  1. 固定數(shù)組的棧實(shí)現(xiàn):
    public ArrayStack(int initSize) {
            if (initSize < 0) {
                throw new IllegalArgumentException("The init size is less than 0");
            }
            arr = new Integer[initSize];
            size = 0;
        }

        public Integer peek() {
            if (size == 0) {
                return null;
            }
            return arr[size - 1];
        }

        public void push(int obj) {
            if (size == arr.length) {
                throw new ArrayIndexOutOfBoundsException("The queue is full");
            }
            arr[size++] = obj;
        }

        public Integer pop() {
            if (size == 0) {
                throw new ArrayIndexOutOfBoundsException("The queue is empty");
            }
            return arr[--size];
        }
    }
  • 隊(duì)列結(jié)構(gòu),先入先出掩驱。

使用數(shù)組設(shè)計(jì)一個(gè)固定長度的隊(duì)列結(jié)構(gòu)芒划,隊(duì)列有固定長度的size,設(shè)計(jì)兩個(gè)指針first,last欧穴。

last:最新添加的數(shù)據(jù)的位置
first:彈出的數(shù)據(jù)的位置

push:往隊(duì)列結(jié)構(gòu)中加入數(shù)據(jù)
poll:從隊(duì)列頭中彈出數(shù)據(jù)民逼,只彈出最早入隊(duì)列的數(shù)據(jù),彈出的數(shù)據(jù)不屬于結(jié)構(gòu)
peek:返回隊(duì)列的第一個(gè)數(shù)據(jù),并不刪除數(shù)據(jù)

  1. 固定數(shù)組實(shí)現(xiàn)隊(duì)列
    public static class ArrayQueue {
        private Integer[] arr;
        private Integer size;
        private Integer first;
        private Integer last;

        public ArrayQueue(int initSize) {
            if (initSize < 0) {
                throw new IllegalArgumentException("The init size is less than 0");
            }
            arr = new Integer[initSize];
            size = 0;
            first = 0;
            last = 0;
        }

        public Integer peek() {
            if (size == 0) {
                return null;
            }
            return arr[first];
        }

        public void push(int obj) {
            if (size == arr.length) {
                throw new ArrayIndexOutOfBoundsException("The queue is full");
            }
            size++;
            arr[last] = obj;
            last = last == arr.length - 1 ? 0 : last + 1;
        }

        public Integer poll() {
            if (size == 0) {
                throw new ArrayIndexOutOfBoundsException("The queue is empty");
            }
            size--;
            int tmp = first;
            first = first == arr.length - 1 ? 0 : first + 1;
            return arr[tmp];
        }
    }
  • 例子:實(shí)現(xiàn)一個(gè)特殊的棧涮帘,在實(shí)現(xiàn)棧的結(jié)構(gòu)拼苍,返回棧中的最小值
    使用兩個(gè)棧實(shí)現(xiàn):
    Data:正常的數(shù)據(jù)入棧
    Min:第一次入棧數(shù)據(jù)和Data一樣,之后调缨,比較Min的棧頂與新數(shù)據(jù)的大小疮鲫,把最小值放入min中,Min和Data的長度一樣弦叶,Min和Data同步彈出俊犯,Min的棧頂就是Data中的最小值。

  • 例子:使用隊(duì)列替換棧結(jié)構(gòu)

隊(duì)列是先進(jìn)先出結(jié)構(gòu)伤哺,棧是先進(jìn)后出結(jié)構(gòu)燕侠。
方法:使用時(shí)兩個(gè)隊(duì)列Queue1和Queue2,正常入棧放到Queue1立莉,當(dāng)要出棧绢彤,就把Queue1最后一個(gè)之前的數(shù)據(jù)放入Queue2,把最后一個(gè)數(shù)據(jù)彈出蜓耻,彈出之后,新數(shù)據(jù)直接放到Queue2茫舶,這樣交替進(jìn)行,swap指向隊(duì)列的指針刹淌。

public static class TwoQueuesStack {
        private Queue<Integer> queue;
        private Queue<Integer> help;

        public TwoQueuesStack() {
            queue = new LinkedList<Integer>();
            help = new LinkedList<Integer>();
        }

        public void push(int pushInt) {
            queue.add(pushInt);
        }

        public int peek() {
            if (queue.isEmpty()) {
                throw new RuntimeException("Stack is empty!");
            }
            while (queue.size() != 1) {
                help.add(queue.poll());
            }
            int res = queue.poll();
            help.add(res);
            swap();
            return res;
        }

        public int pop() {
            if (queue.isEmpty()) {
                throw new RuntimeException("Stack is empty!");
            }
            while (queue.size() > 1) {
                help.add(queue.poll());
            }
            int res = queue.poll();
            swap();
            return res;
        }

        private void swap() {
            Queue<Integer> tmp = help;
            help = queue;
            queue = tmp;
        }

    }
  • 例子:使用棧替換隊(duì)列結(jié)構(gòu)

隊(duì)列是先進(jìn)先出結(jié)構(gòu)饶氏,棧是先進(jìn)后出結(jié)構(gòu)。

同樣使用兩個(gè)棧結(jié)構(gòu)Stack1和Stack2芦鳍,使用時(shí)往Stack1添加數(shù)據(jù)嚷往,第一次彈數(shù)據(jù)時(shí)葛账,把Stack1數(shù)據(jù)全部放入Stack2柠衅,從Stack2彈出數(shù)據(jù),之后把添加數(shù)據(jù)放入Stack1籍琳,Stack2一直彈數(shù)據(jù)菲宴,直到Stack2為空時(shí)再把Stack1倒入Stack2贷祈,所以Stack1一直是添加數(shù)據(jù)棧,Stack2存儲(chǔ)的是最先入隊(duì)的數(shù)據(jù)喝峦。

public static class TwoStacksQueue {
        private Stack<Integer> stackPush;
        private Stack<Integer> stackPop;

        public TwoStacksQueue() {
            stackPush = new Stack<Integer>();
            stackPop = new Stack<Integer>();
        }

        public void push(int pushInt) {
            stackPush.push(pushInt);
        }

        public int poll() {
            if (stackPop.empty() && stackPush.empty()) {
                throw new RuntimeException("Queue is empty!");
            } else if (stackPop.empty()) {
                while (!stackPush.empty()) {
                    stackPop.push(stackPush.pop());
                }
            }
            return stackPop.pop();
        }

        public int peek() {
            if (stackPop.empty() && stackPush.empty()) {
                throw new RuntimeException("Queue is empty!");
            } else if (stackPop.empty()) {
                while (!stackPush.empty()) {
                    stackPop.push(stackPush.pop());
                }
            }
            return stackPop.peek();
        }
    }

二叉樹結(jié)構(gòu)

有左右兩個(gè)子節(jié)點(diǎn)left势誊, right,同時(shí)函數(shù)有數(shù)據(jù)data

實(shí)現(xiàn)二叉樹結(jié)構(gòu):

public static class Node {
        public int value;
        public Node left;
        public Node right;

        public Node(int data) {
            this.value = data;
        }
    }

二叉樹遍歷一顆樹每個(gè)節(jié)點(diǎn)會(huì)訪問三次谣蠢。

    1. 先序遍歷
      先打印節(jié)點(diǎn)粟耻,再打印左子樹,之后為右子樹
    1. 中序遍歷
      先打印左子樹眉踱,再打印節(jié)點(diǎn)挤忙,之后為右子樹
    1. 后序遍歷
      先打印左子樹,在打印為右子樹谈喳,之后打印節(jié)點(diǎn)
      遞歸函數(shù):
public static void preOrderRecur(Node head) {
        if (head == null) {
            return;
        }
        System.out.print(head.value + " ");
        preOrderRecur(head.left);
        preOrderRecur(head.right);
    }

    public static void inOrderRecur(Node head) {
        if (head == null) {
            return;
        }
        inOrderRecur(head.left);
        System.out.print(head.value + " ");
        inOrderRecur(head.right);
    }

    public static void posOrderRecur(Node head) {
        if (head == null) {
            return;
        }
        posOrderRecur(head.left);
        posOrderRecur(head.right);
        System.out.print(head.value + " ");
    }

解遞歸函數(shù):

  1. 前序遍歷册烈,可以通過棧實(shí)現(xiàn),先把頭部壓棧婿禽,先彈打印當(dāng)前節(jié)點(diǎn)赏僧,先壓右節(jié)點(diǎn),再壓左節(jié)點(diǎn)扭倾,如果無子節(jié)點(diǎn)淀零,進(jìn)行彈出,彈出左節(jié)點(diǎn)進(jìn)行打印膛壹,之后右節(jié)點(diǎn)窑滞。
public static void preOrderUnRecur(Node head) {
        System.out.print("pre-order: ");
        if (head != null) {
            Stack<Node> stack = new Stack<Node>();
            stack.add(head);
            while (!stack.isEmpty()) {
                head = stack.pop();
                System.out.print(head.value + " ");
                if (head.right != null) {
                    stack.push(head.right);
                }
                if (head.left != null) {
                    stack.push(head.left);
                }
            }
        }
        System.out.println();
    }

2.中序遍歷把每個(gè)節(jié)點(diǎn)的左節(jié)點(diǎn)壓棧,直到遇到空恢筝,進(jìn)行出棧進(jìn)行彈出打印哀卫,找到同一節(jié)點(diǎn)的右節(jié)點(diǎn),進(jìn)行左節(jié)點(diǎn)壓棧撬槽,循環(huán)此改。。侄柔。共啃。

public static void inOrderUnRecur(Node head) {
        System.out.print("in-order: ");
        if (head != null) {
            Stack<Node> stack = new Stack<Node>();
            while (!stack.isEmpty() || head != null) {
                if (head != null) {
                    stack.push(head);
                    head = head.left;
                } else {
                    head = stack.pop();
                    System.out.print(head.value + " ");
                    head = head.right;
                }
            }
        }
        System.out.println();
    }
  1. 后序遍歷
    使用前序遍歷的逆序,使用兩個(gè)棧暂题,一個(gè)棧存頭部移剪,一個(gè)棧存打印節(jié)點(diǎn),先壓左節(jié)點(diǎn)薪者,不進(jìn)行打印而是進(jìn)行壓棧纵苛,再壓右節(jié)點(diǎn),不進(jìn)行打印而是進(jìn)行壓棧。完全遍歷之后進(jìn)行打印攻人。
    public static void posOrderUnRecur1(Node head) {
        System.out.print("pos-order: ");
        if (head != null) {
            Stack<Node> s1 = new Stack<Node>();
            Stack<Node> s2 = new Stack<Node>();
            s1.push(head);
            while (!s1.isEmpty()) {
                head = s1.pop();
                s2.push(head);
                if (head.left != null) {
                    s1.push(head.left);
                }
                if (head.right != null) {
                    s1.push(head.right);
                }
            }
            while (!s2.isEmpty()) {
                System.out.print(s2.pop().value + " ");
            }
        }
        System.out.println();
    }
  • 樹的序列化
    前序的序列化
    對(duì)每個(gè)子節(jié)點(diǎn)取试,先返回左節(jié)點(diǎn),在返回右節(jié)點(diǎn)怀吻,如果遇到null瞬浓,以"#!"代替
public static String serialByPre(Node head) {
        if (head == null) {
            return "#!";
        }
        String res = head.value + "!";
        res += serialByPre(head.left);
        res += serialByPre(head.right);
        return res;
    }
  • 判斷一個(gè)二叉樹是否是平衡二叉樹

平衡二叉樹,左右兩個(gè)邊的高度相差不超過1蓬坡。

  1. 左樹平衡猿棉,右樹平衡
  2. 左樹的高度,右樹的高度

遞歸函數(shù)每次返回?cái)?shù)據(jù)結(jié)構(gòu)是一樣的屑咳,通過先判度是否平衡铺根,再判斷右樹高度。

public static boolean isBalance(Node head) {
        boolean[] res = new boolean[1];
        res[0] = true;
        getHeight(head, 1, res);
        return res[0];
    }

    public static int getHeight(Node head, int level, boolean[] res) {
        if (head == null) {
            return level;
        }
        int lH = getHeight(head.left, level + 1, res);
        if (!res[0]) {
            return level;
        }
        int rH = getHeight(head.right, level + 1, res);
        if (!res[0]) {
            return level;
        }
        if (Math.abs(lH - rH) > 1) {
            res[0] = false;
        }
        return Math.max(lH, rH);
    }
  • 搜索二叉樹

對(duì)一顆樹乔宿,左子節(jié)點(diǎn)的值比右子節(jié)點(diǎn)大位迂。在中序遍歷中,通過判斷先前節(jié)點(diǎn)==當(dāng)前節(jié)點(diǎn)详瑞。

  • 判斷完全二叉樹
  1. 判斷有右節(jié)點(diǎn)掂林,無節(jié)點(diǎn),為false
  2. 同一層中間層有缺節(jié)點(diǎn)
public static boolean isCBT(Node head) {
        if (head == null) {
            return true;
        }
        Queue<Node> queue = new LinkedList<Node>();
        boolean leaf = false;
        Node l = null;
        Node r = null;
        queue.offer(head);
        while (!queue.isEmpty()) {
            head = queue.poll();
            l = head.left;
            r = head.right;
            if ((leaf && (l != null || r != null)) || (l == null && r != null)) {
                return false;
            }
            if (l != null) {
                queue.offer(l);
            }
            if (r != null) {
                queue.offer(r);
            } else {
                leaf = true;
            }
        }
        return true;
    }
  • 一知一顆完全二叉樹坝橡,求其節(jié)點(diǎn)的個(gè)數(shù)

曼兒叉樹節(jié)點(diǎn)個(gè)數(shù)L泻帮,h(高度):L = 2^(h) - 1

hash 函數(shù)

  • hash(散列、雜湊)函數(shù)计寇,是將任意長度的數(shù)據(jù)映射到有限長度的域上锣杂。直觀解釋起來,就是對(duì)一串?dāng)?shù)據(jù)m進(jìn)行雜糅番宁,輸出另一段固定長度的數(shù)據(jù)h元莫,作為這段數(shù)據(jù)的特征(指紋)。
  1. input ==無窮
  2. output == 有限
  3. input same == out same
  4. input not same == out same 蝶押, hash碰撞
  5. input 無窮 == output 均勻分布

hash函數(shù)的輸出值踱蠢,每個(gè)位置上的值均是獨(dú)立的。

  • hash 表

一個(gè)矩陣中只有0棋电,1兩種值茎截,每個(gè)位置都可以和自己的上下左右,四個(gè)位置相連赶盔,如果有一片1連在一起企锌,這個(gè)部分叫做一個(gè)島,求矩陣中有多少個(gè)島于未。

0 1 1 0 1 0
0 1 1 0 1 0
1 0 1 0 0 0
0 0 1 0 1 0

使用感染函數(shù)撕攒,把已經(jīng)感染的值1的上下左右變成2陡鹃,島的數(shù)量加1,繼續(xù)繼續(xù)重新遍歷打却,跳過0和2,循環(huán)遍歷谎倔。

public static int countIslands(int[][] m) {
        if (m == null || m[0] == null) {
            return 0;
        }
        int N = m.length;
        int M = m[0].length;
        int res = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (m[i][j] == 1) {
                    res++;
                    infect(m, i, j, N, M);
                }
            }
        }
        return res;
    }

    public static void infect(int[][] m, int i, int j, int N, int M) {
        if (i < 0 || i >= N || j < 0 || j >= M || m[i][j] != 1) {
            return;
        }
        m[i][j] = 2;
        infect(m, i + 1, j, N, M);
        infect(m, i - 1, j, N, M);
        infect(m, i, j + 1, N, M);
        infect(m, i, j - 1, N, M);
    }

集合

  • 查找兩個(gè)數(shù)是不是同一個(gè)集合:
    通過進(jìn)行集合的合并柳击,給一個(gè)[1,2,3,4],每個(gè)數(shù)是自己的集合,[[1,], [2,], [3,], [4,]],如下片习,當(dāng)把[1]和[2]進(jìn)行合并時(shí)捌肴,把[2] 掛到[1]的集合上。

前綴樹

在進(jìn)行建樹的過程中藕咏,把字母依次在樹的路徑上進(jìn)行標(biāo)記状知,如果某一條路徑不存在,就建立一條路徑孽查。

使用前綴樹進(jìn)行字符的查找饥悴,構(gòu)建方法insert, delete,search進(jìn)前綴樹的構(gòu)建。
insert:是在數(shù)據(jù)樹中插入數(shù)據(jù)盲再,delete:刪除數(shù)據(jù)西设,search:在樹查找字符。
insert:沿著路徑進(jìn)行查找答朋,當(dāng)路徑不存在時(shí)構(gòu)建路徑贷揽,如果以及存在path++
delete:刪除路徑上的數(shù)據(jù)蓖柔,如果path==0,可以直接刪除之后的結(jié)果僚害,如果path!=0侣肄,需要?jiǎng)h除對(duì)應(yīng)數(shù)據(jù)洪规。
search:通過word進(jìn)行數(shù)據(jù)查找印屁。

    public static class TrieNode {
        public int path;
        public int end;
        public TrieNode[] nexts;

        public TrieNode() {
            path = 0;
            end = 0;
            nexts = new TrieNode[26];
        }
    }

    public static class Trie {
        private TrieNode root;

        public Trie() {
            root = new TrieNode();
        }

        public void insert(String word) {
            if (word == null) {
                return;
            }
            char[] chs = word.toCharArray();
            TrieNode node = root;
            int index = 0;
            for (int i = 0; i < chs.length; i++) {
                index = chs[i] - 'a';
                if (node.nexts[index] == null) {
                    node.nexts[index] = new TrieNode();
                }
                node = node.nexts[index];
                node.path++;
            }
            node.end++;
        }

        public void delete(String word) {
            if (search(word) != 0) {
                char[] chs = word.toCharArray();
                TrieNode node = root;
                int index = 0;
                for (int i = 0; i < chs.length; i++) {
                    index = chs[i] - 'a';
                    if (--node.nexts[index].path == 0) {
                        node.nexts[index] = null;
                        return;
                    }
                    node = node.nexts[index];
                }
                node.end--;
            }
        }

        public int search(String word) {
            if (word == null) {
                return 0;
            }
            char[] chs = word.toCharArray();
            TrieNode node = root;
            int index = 0;
            for (int i = 0; i < chs.length; i++) {
                index = chs[i] - 'a';
                if (node.nexts[index] == null) {
                    return 0;
                }
                node = node.nexts[index];
            }
            return node.end;
        }

        public int prefixNumber(String pre) {
            if (pre == null) {
                return 0;
            }
            char[] chs = pre.toCharArray();
            TrieNode node = root;
            int index = 0;
            for (int i = 0; i < chs.length; i++) {
                index = chs[i] - 'a';
                if (node.nexts[index] == null) {
                    return 0;
                }
                node = node.nexts[index];
            }
            return node.path;
        }
    }

貪心算法的排序

if str1 + str2 < str2 + str1,str2 > str1

切分金條

子節(jié)點(diǎn)合并在一起的是葉節(jié)點(diǎn)的和斩例,怎么加最經(jīng)濟(jì)库车。

最大收益

有初始資金==w=10 ,有一組項(xiàng)目[[10,2],[12,3],[7,5],[,5,3],[13,10]]
[10, 2]:10為本金樱拴,2為利潤

家里兩個(gè)堆:
小根堆:按本金進(jìn)行排放柠衍,最小自放在最上面,都是現(xiàn)在無法完成的項(xiàng)目晶乔,沒錢
大根堆:按利潤進(jìn)行排放珍坊,最大的放上面,都是可以做的項(xiàng)目正罢。

從大根堆開始做項(xiàng)目阵漏,每次做最上的一個(gè)項(xiàng)目,做完之后把小根堆可以做的項(xiàng)目添加到大根堆上,直到k次或者大根堆已經(jīng)沒有項(xiàng)目可做結(jié)束履怯。

public static class MinCostComparator implements Comparator<Node> {

        @Override
        public int compare(Node o1, Node o2) {
            return o1.c - o2.c;
        }

    }

    public static class MaxProfitComparator implements Comparator<Node> {

        @Override
        public int compare(Node o1, Node o2) {
            return o2.p - o1.p;
        }

    }

    public static int findMaximizedCapital(int k, int W, int[] Profits, int[] Capital) {
        Node[] nodes = new Node[Profits.length];
        for (int i = 0; i < Profits.length; i++) {
            nodes[i] = new Node(Profits[i], Capital[i]);
        }

        PriorityQueue<Node> minCostQ = new PriorityQueue<>(new MinCostComparator());
        PriorityQueue<Node> maxProfitQ = new PriorityQueue<>(new MaxProfitComparator());
        for (int i = 0; i < nodes.length; i++) {
            minCostQ.add(nodes[i]);
        }
        for (int i = 0; i < k; i++) {
            while (!minCostQ.isEmpty() && minCostQ.peek().c <= W) {
                maxProfitQ.add(minCostQ.poll());
            }
            if (maxProfitQ.isEmpty()) {
                return W;
            }
            W += maxProfitQ.poll().p;
        }
        return W;
    }

動(dòng)態(tài)規(guī)劃

n回还!

同過解依賴問題n! = n * (n-1)! = n*(n-1)...1!
通過

'abc'

打印‘a(chǎn)bc’,打印所有字符組合叹洲。有三個(gè)位置柠硕,要a,不要a,要b运提,不要b,要c蝗柔,不要c。2x2x2=8種

public static void printAllSubsquence(String str) {
        char[] chs = str.toCharArray();
        process(chs, 0);
    }

    public static void process(char[] chs, int i) {
        if (i == chs.length) {
            System.out.println(String.valueOf(chs));
            return;
        }
        process(chs, i + 1);
        char tmp = chs[i];
        chs[i] = 0;
        process(chs, i + 1);
        chs[i] = tmp;
    }
  • 母牛每年生一只母牛民泵,新出生的母牛成長三年后也能每年生一只 母牛癣丧,假設(shè)不會(huì)死。求N年后栈妆,母牛的數(shù)胁编。
    n-1年和n-3年的牛綜合滿足第n年的類型,需要列出所有的牛的總數(shù)鳞尔,寫出所有的初始項(xiàng)掏呼。

F(n) = F(n-1) + F(n-3)

    public static int cowNumber1(int n) {
        if (n < 1) {
            return 0;
        }
        if (n == 1 || n == 2 || n == 3) {
            return n;
        }
        return cowNumber1(n - 1) + cowNumber1(n - 3);
    }
  • 給你一個(gè)二維數(shù)組,二維數(shù)組中的每個(gè)數(shù)都是正數(shù)铅檩,要求從左上 角走到右下角憎夷,每一步只能向右或者向下。沿途經(jīng)過的數(shù)字要累 加起來昧旨。返回最小的路徑和
    public static int process1(int[][] matrix, int i, int j) {
    int res = matrix[i][j];
    if (i == 0 && j == 0) {
    return res;
    }
    if (i == 0 && j != 0) {
    return res + process1(matrix, i, j - 1);
    }
    if (i != 0 && j == 0) {
    return res + process1(matrix, i - 1, j);
    }
    return res + Math.min(process1(matrix, i, j - 1), process1(matrix, i - 1, j));
    }

通過構(gòu)建dp表拾给,因?yàn)閺?i,j)點(diǎn)到(n,n)的值固定,所以通過構(gòu)建dp樹可以進(jìn)行dp通過解所有的dp表就可以解出最小值.

3 1 0 2
4 3 2 1
5 2 1 0

bp表:

(i,j) (i,j+1) * 3
(i+1,j) * * 1
8 3 1 0

通過化解遞歸兔沃,使用動(dòng)態(tài)規(guī)劃:

    public static int minPath2(int[][] m) {
        if (m == null || m.length == 0 || m[0] == null || m[0].length == 0) {
            return 0;
        }
        int row = m.length;
        int col = m[0].length;
        int[][] dp = new int[row][col];
        dp[0][0] = m[0][0];
        for (int i = 1; i < row; i++) {
            dp[i][0] = dp[i - 1][0] + m[i][0];
        }
        for (int j = 1; j < col; j++) {
            dp[0][j] = dp[0][j - 1] + m[0][j];
        }
        for (int i = 1; i < row; i++) {
            for (int j = 1; j < col; j++) {
                dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + m[i][j];
            }
        }
        return dp[row - 1][col - 1];
    }
  • 給你一個(gè)數(shù)組arr蒋得,和一個(gè)整數(shù)aim。如果可以任意選擇arr中的 數(shù)字乒疏,能不能累加得到aim额衙,返回true或者false2

在數(shù)組中,一個(gè)數(shù)字怕吴,有要和不要窍侧,如果要就加上這個(gè)數(shù)組,如果得到aim转绷,返回true伟件。

    public static boolean money1(int[] arr, int aim) {
        return process1(arr, 0, 0, aim);
    }

    public static boolean process1(int[] arr, int i, int sum, int aim) {
        if (sum == aim) {
            return true;
        }
        // sum != aim
        if (i == arr.length) {
            return false;
        }
        return process1(arr, i + 1, sum, aim) || process1(arr, i + 1, sum + arr[i], aim);
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市议经,隨后出現(xiàn)的幾起案子斧账,更是在濱河造成了極大的恐慌谴返,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咧织,死亡現(xiàn)場離奇詭異嗓袱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)习绢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門渠抹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人毯炮,你說我怎么就攤上這事逼肯∷屎冢” “怎么了桃煎?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長大刊。 經(jīng)常有香客問我为迈,道長,這世上最難降的妖魔是什么缺菌? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任葫辐,我火速辦了婚禮,結(jié)果婚禮上伴郁,老公的妹妹穿的比我還像新娘耿战。我一直安慰自己,他們只是感情好焊傅,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布剂陡。 她就那樣靜靜地躺著,像睡著了一般狐胎。 火紅的嫁衣襯著肌膚如雪鸭栖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天握巢,我揣著相機(jī)與錄音晕鹊,去河邊找鬼。 笑死暴浦,一個(gè)胖子當(dāng)著我的面吹牛溅话,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播歌焦,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼公荧,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了同规?” 一聲冷哼從身側(cè)響起循狰,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤窟社,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后绪钥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灿里,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年程腹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了匣吊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寸潦,死狀恐怖色鸳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情见转,我是刑警寧澤命雀,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站斩箫,受9級(jí)特大地震影響吏砂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乘客,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一狐血、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧易核,春花似錦匈织、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至井氢,卻和暖如春弦追,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背花竞。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來泰國打工劲件, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人约急。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓零远,卻偏偏與公主長得像,于是被迫代替她去往敵國和親厌蔽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子牵辣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容