数据结构算法总结

冒泡排序

原理:比较两个相邻的元素,将值大的元素交换至右端。

思路:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。重复第一趟步骤,直至全部排序完成。

        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实现,每一趟的长度都应该减少1,第一趟时要对比所有的数据,从arr[0]到arr[-1]进行对比,最后一次的arr[j+1]就是arr[-1],以后依次减少对比的长度。

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个待排序的元素看成为一个有序表和一个无序表。开始时有序表中只包含1个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。
从后面的无序表中拿出一个数,先与最后有序表最后一个元素进行对比,如果arr[j] > arr[j + 1],就进行插入,否则不进行操作。

        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实现,每次有序长度加1或者说每次都需要从无序列表中拿一个数据,进行插入对比是在有序里面进行的,第一次开始的时arr[j+1]就是最新加入的数据

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]

选择排序

原理:每一趟从待排序的记录中选出最小的元素,顺序放在已排好序的序列最后,直到全部记录排序完毕。
给定数组:int[] arr={里面n个数据};第1趟排序,在待排序数据arr[1]~ arr[n]中选出最小的数据,将它与arrr[1]交换;第2趟,在待排序数据arr[2]~ arr[n]中选出最小的数据,将它与r[2]交换;以此类推,第i趟在待排序数据arr[i]~ arr[n]中选出最小的数据,将它与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);
        }

二分法使用递归查找最大值

使用堆的压栈和出栈理解递归函数,每次调用一次自己,就会把一个getmax(arr, L, R)的信息压入栈,这时会有多个getmax(arr, L, R)函数被压入栈,每个getmax(arr, L, R)有自己的独立信息,当L == R时,就会执行出栈操作,从上往下进行出栈操作。
getmax:是通过二分法取得左侧和右侧的最大值,之后进最大值之间的比较。

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)
}

并归排序

使用递归进行二分法进行排序,把左侧和右侧单独排序,之后通过外派进行进行排序。外排就是使用两个指针,新建一个新数组,一个指向左侧第一个,一个指向右侧第一个,把两个指针的小值填入新数组,填入的指针加一。
解释:void mergeSort(int[] arr, int l, int r)就是二分法的递归函数把左侧和右侧进行排序
使用merge(int[] arr, int l, int m, int r)进行外派合并,每次压栈只对arr的[l, r]进行操作,其他地方不变。

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];
        }
    }

荷兰国旗问题

输入一个数组arr和一个数字p,把小于p的数放左边,大于p的数据放右边,相等的放中间。

思路:设置一个less指针和more指针。less指向arr[0],more指向arr[-1],l从0-length滑动。如果arr[l] < p就 交换arr[less+1]和arr[l], 之后i = i +1,如果arr[l] > p就交换arr[more-1]和arr[l],重新判断i位置,相等时直接 i = i +1.
less + 1, more - 1 是arr[l] == p区间的位置

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 实现荷兰国旗问题,这里与四个指针,left,right时原始的数组开始和结尾指针,less和more是在arr中小于和大于num的边界,less和more中间的值就是等于num的值。在排序过程中,less左右边的值必为小于num的值,在more右的值必为大于num的值,指针left每次都加1,但如果遇到大于num时,会把当前的值和一个未知大小的值arr[more-1]进行交换,所以需要重新判断一次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)

快排

使用递归划分子集,给定一个arr,首先arr取中的最后一个数arr[-1]。把arr[l] < arr[-1]的数放左边,arr[l] > arr[-1]的数放右边,arr[l] == arr[-1]的数放中间。得到arr[l] == arr[-1]的边界p,p[0] - 1就是arr[l] < arr[-1]的数表,p[1] + 1就是arr[l] > arr[-1]的数表,继续进递归,中间arr[l] == arr[-1]的不需要动,递归操作的是l-r, 递归返回的条件就是arr[-1]就是整数组的最大值,这时l和p[0]相等,p[1]和r相等,或者p[0]==p[1]相等。
随机快排,由于数组本身就是有序数据,这时复杂度为最大,随机选取一个数放在arr[-1],就可以以随机概率在大概率情况下取得较小复杂度。

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); // 由于最后一个数不参与划分直接,需要把r放入等于区间
        return new int[] { less + 1, more };
    }

通过把每个子数组的进行二分,把小于arr[-1]放左边,把大于arr[-1]的放右边,当数组中被切分的[left, right]最大值被放到最左边时,就可进行返回了。

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))

堆结构

  • 把一个数组建一个大根堆结构。
    i 左节点:2 * i + 1
    i 右节点: 2 * i + 1
    父结构: (i - 1) / 2
    所有节点都不能越界。
    完全二叉树:先补左节点,再补右节点
    大根堆:完全二叉树,任何一个子树的最大值为子树头部。
    小根堆:完全二叉树,任何一个子树的最小值为子树头部。

  • 对数组arr,每次添加一个数到堆里。

heapInsert(int[] arr, int index) 第index个数加到堆的操作:找到index的父节点,对比其中的最大值,如果arr[index] > arr[(index - 1) / 2],第index个数比父节点大,进行交换,之后与父父节点对比,直到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;
        }
    }
  • 如果堆的值发生改变:
    需要改变堆的结构,把小值进行下沉。
    在一个数组中index的值发生改变,arr中size为堆的长度。
    找到index的两个子节点中最大值largest,比较largest和index的小,如果largest <= index返回,否则交换swap(arr, largest, index),进入下一个子节点的比较
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;
        }
    }
  • 怎么进行减堆操作:
    1.删掉最后值,2.弹出第一个值,把最后一个值放到第一个值,调整堆结构

对一个动态的数组建立一个堆,注意:数组的长度不固定,通过添加值的同时就可以保持数据的有序性。

通过建立一个大根堆和一个小根堆,两个堆列的size不能超过1,如果某一个num <= big[0]进行入大根堆,否则进入小根堆,size相差超过1,弹出头部,这时大根堆的头部为中位数。

堆排序

基于大根堆进行排序,因为每个大根堆的头部最大值是整个堆的最大值,通过把头部与尾部进行交换,size-1,进行对调整,循环调整。

首先建堆,把数组arr堆成堆arr,通过交换头部与尾部,再调整堆结构。

        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);
        }

栈结构

  • 先入后出的一种数据结构,有push,pop,peek操作。
    push:往栈结构中加入数据
    pop:从栈结构中弹出数据,只弹出最新的数据,弹出的数据不属于栈结构
    peek:返回栈的最后一个数据,并不删除数据
  1. 固定数组的栈实现:
    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];
        }
    }
  • 队列结构,先入先出。

使用数组设计一个固定长度的队列结构,队列有固定长度的size,设计两个指针first,last。

last:最新添加的数据的位置
first:弹出的数据的位置

push:往队列结构中加入数据
poll:从队列头中弹出数据,只弹出最早入队列的数据,弹出的数据不属于结构
peek:返回队列的第一个数据,并不删除数据

  1. 固定数组实现队列
    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];
        }
    }
  • 例子:实现一个特殊的栈,在实现栈的结构,返回栈中的最小值
    使用两个栈实现:
    Data:正常的数据入栈
    Min:第一次入栈数据和Data一样,之后,比较Min的栈顶与新数据的大小,把最小值放入min中,Min和Data的长度一样,Min和Data同步弹出,Min的栈顶就是Data中的最小值。

  • 例子:使用队列替换栈结构

队列是先进先出结构,栈是先进后出结构。
方法:使用时两个队列Queue1和Queue2,正常入栈放到Queue1,当要出栈,就把Queue1最后一个之前的数据放入Queue2,把最后一个数据弹出,弹出之后,新数据直接放到Queue2,这样交替进行,swap指向队列的指针。

public static class TwoQueuesStack {
        private Queue queue;
        private Queue help;

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

        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 tmp = help;
            help = queue;
            queue = tmp;
        }

    }
  • 例子:使用栈替换队列结构

队列是先进先出结构,栈是先进后出结构。

同样使用两个栈结构Stack1和Stack2,使用时往Stack1添加数据,第一次弹数据时,把Stack1数据全部放入Stack2,从Stack2弹出数据,之后把添加数据放入Stack1,Stack2一直弹数据,直到Stack2为空时再把Stack1倒入Stack2,所以Stack1一直是添加数据栈,Stack2存储的是最先入队的数据。

public static class TwoStacksQueue {
        private Stack stackPush;
        private Stack stackPop;

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

        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();
        }
    }

二叉树结构

有左右两个子节点left, right,同时函数有数据data

实现二叉树结构:

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

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

二叉树遍历一颗树每个节点会访问三次。

    1. 先序遍历
      先打印节点,再打印左子树,之后为右子树
    1. 中序遍历
      先打印左子树,再打印节点,之后为右子树
    1. 后序遍历
      先打印左子树,在打印为右子树,之后打印节点
      递归函数:
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 + " ");
    }

解递归函数:

  1. 前序遍历,可以通过栈实现,先把头部压栈,先弹打印当前节点,先压右节点,再压左节点,如果无子节点,进行弹出,弹出左节点进行打印,之后右节点。
public static void preOrderUnRecur(Node head) {
        System.out.print("pre-order: ");
        if (head != null) {
            Stack stack = new Stack();
            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.中序遍历把每个节点的左节点压栈,直到遇到空,进行出栈进行弹出打印,找到同一节点的右节点,进行左节点压栈,循环。。。。

public static void inOrderUnRecur(Node head) {
        System.out.print("in-order: ");
        if (head != null) {
            Stack stack = new Stack();
            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. 后序遍历
    使用前序遍历的逆序,使用两个栈,一个栈存头部,一个栈存打印节点,先压左节点,不进行打印而是进行压栈,再压右节点,不进行打印而是进行压栈。完全遍历之后进行打印。
    public static void posOrderUnRecur1(Node head) {
        System.out.print("pos-order: ");
        if (head != null) {
            Stack s1 = new Stack();
            Stack s2 = new Stack();
            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();
    }
  • 树的序列化
    前序的序列化
    对每个子节点,先返回左节点,在返回右节点,如果遇到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;
    }
  • 判断一个二叉树是否是平衡二叉树

平衡二叉树,左右两个边的高度相差不超过1。

  1. 左树平衡,右树平衡
  2. 左树的高度,右树的高度

递归函数每次返回数据结构是一样的,通过先判度是否平衡,再判断右树高度。

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);
    }
  • 搜索二叉树

对一颗树,左子节点的值比右子节点大。在中序遍历中,通过判断先前节点==当前节点。

  • 判断完全二叉树
  1. 判断有右节点,无节点,为false
  2. 同一层中间层有缺节点
public static boolean isCBT(Node head) {
        if (head == null) {
            return true;
        }
        Queue queue = new LinkedList();
        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;
    }
  • 一知一颗完全二叉树,求其节点的个数

曼儿叉树节点个数L,h(高度):L = 2^(h) - 1

hash 函数

  • hash(散列、杂凑)函数,是将任意长度的数据映射到有限长度的域上。直观解释起来,就是对一串数据m进行杂糅,输出另一段固定长度的数据h,作为这段数据的特征(指纹)。
  1. input ==无穷
  2. output == 有限
  3. input same == out same
  4. input not same == out same , hash碰撞
  5. input 无穷 == output 均匀分布

hash函数的输出值,每个位置上的值均是独立的。

  • hash 表

一个矩阵中只有0,1两种值,每个位置都可以和自己的上下左右,四个位置相连,如果有一片1连在一起,这个部分叫做一个岛,求矩阵中有多少个岛。

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

使用感染函数,把已经感染的值1的上下左右变成2,岛的数量加1,继续继续重新遍历,跳过0和2,循环遍历。

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);
    }

集合

  • 查找两个数是不是同一个集合:
    通过进行集合的合并,给一个[1,2,3,4],每个数是自己的集合,[[1,], [2,], [3,], [4,]],如下,当把[1]和[2]进行合并时,把[2] 挂到[1]的集合上。

前缀树

在进行建树的过程中,把字母依次在树的路径上进行标记,如果某一条路径不存在,就建立一条路径。

使用前缀树进行字符的查找,构建方法insert, delete,search进前缀树的构建。
insert:是在数据树中插入数据,delete:删除数据,search:在树查找字符。
insert:沿着路径进行查找,当路径不存在时构建路径,如果以及存在path++
delete:删除路径上的数据,如果path==0,可以直接删除之后的结果,如果path!=0,需要删除对应数据。
search:通过word进行数据查找。

    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

切分金条

子节点合并在一起的是叶节点的和,怎么加最经济。

最大收益

有初始资金==w=10 ,有一组项目[[10,2],[12,3],[7,5],[,5,3],[13,10]]
[10, 2]:10为本金,2为利润

家里两个堆:
小根堆:按本金进行排放,最小自放在最上面,都是现在无法完成的项目,没钱
大根堆:按利润进行排放,最大的放上面,都是可以做的项目。

从大根堆开始做项目,每次做最上的一个项目,做完之后把小根堆可以做的项目添加到大根堆上,直到k次或者大根堆已经没有项目可做结束。

public static class MinCostComparator implements Comparator {

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

    }

    public static class MaxProfitComparator implements Comparator {

        @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 minCostQ = new PriorityQueue<>(new MinCostComparator());
        PriorityQueue 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;
    }

动态规划

n!

同过解依赖问题n! = n * (n-1)! = n*(n-1)...1!
通过

'abc'

打印‘abc’,打印所有字符组合。有三个位置,要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;
    }
  • 母牛每年生一只母牛,新出生的母牛成长三年后也能每年生一只 母牛,假设不会死。求N年后,母牛的数。
    n-1年和n-3年的牛综合满足第n年的类型,需要列出所有的牛的总数,写出所有的初始项。

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);
    }
  • 给你一个二维数组,二维数组中的每个数都是正数,要求从左上 角走到右下角,每一步只能向右或者向下。沿途经过的数字要累 加起来。返回最小的路径和
    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));
    }

通过构建dp表,因为从(i,j)点到(n,n)的值固定,所以通过构建dp树可以进行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

通过化解递归,使用动态规划:

    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];
    }
  • 给你一个数组arr,和一个整数aim。如果可以任意选择arr中的 数字,能不能累加得到aim,返回true或者false2

在数组中,一个数字,有要和不要,如果要就加上这个数组,如果得到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);
    }

你可能感兴趣的:(数据结构算法总结)