java算法总结

算法

二叉树

二叉树结构

二叉树(binary tree)t是有限个元素的集合(可以为空)。当二叉树非空时,其中有一个称为根的元素,余下的元素(如果有的话)被组成2个二叉树,分别称为t的左子树和右子树

 class Node {
     private Integer value;
     private Node leftNode;
     private Node rightNode;
 }

前序遍历

中,左,右

 /**
     * 普通前序遍历
     * 不使用栈操作
     * @param node
     */
    public void commonBefore(Node node){
        Node cur = node;
        Node pre;
        while (cur != null){
            if (cur.leftNode == null){
                System.out.println(cur.getValue());
                cur = cur.rightNode;
            }else {
                pre = cur.leftNode;
                while (pre.rightNode != null && pre.rightNode != cur){
                    pre = pre.rightNode;
                }
                if (pre.rightNode == null){
                    System.out.println(cur.getValue());
                    pre.rightNode = cur;
                    cur = cur.leftNode;
                }else {
                    pre.rightNode = null;
                    cur = cur.rightNode;
                }

            }
        }
    }
    
    /**
     * 遍历实现 前序
     * @param root
     */
    public void traverseBefore(Node root){
        LinkedList<Node> list = new LinkedList<>();
        Node currentNode = root;
        //从根开始,一只先访问左子树,再访问右子树,退出某一层级时当前栈顶则为根节点
        while (currentNode != null || !list.isEmpty()){
            while (currentNode != null){
                System.out.println(currentNode.getValue());
                list.push(currentNode);
                currentNode = currentNode.leftNode;
            }
            currentNode = list.pop().rightNode;
        }
    }
    
    /**
     * 先序遍历,迭代实现
     */
    public void recursiveBefore(Node node){
        if (node == null){
            return;
        }
        System.out.println(node.getValue());
        recursiveBefore(node.leftNode);
        recursiveBefore(node.rightNode);
    }

中序遍历

左,中,右

/**
     * 普通中序遍历
     * 不使用栈操作
     * @param node
     */
    public void commonMid(Node node){
        Node cur = node;
        Node pre ;
        while (cur != null){
            if (cur.leftNode == null){
                System.out.println(cur.getValue());
                cur = cur.rightNode;
            }else {
                pre = cur.leftNode;
                while (pre.rightNode != null && pre.rightNode != cur){
                    pre = pre.rightNode;
                }
                if (pre.rightNode == null){
                    pre.rightNode = cur;
                    cur = cur.leftNode;
                }else {
                    System.out.println(cur.getValue());
                    pre.rightNode = null;
                    cur = cur.rightNode;
                }

            }
        }
    }
    /**
     * 遍历实现 中序
     * @param root
     */
    public void traverseMid(Node root){
        LinkedList<Node> list = new LinkedList<>();
        Node currentNode = root;
        //从根开始,一只先访问左子树,再访问父节点,再访问右子树,退出某一层级时当前栈顶则为根节点
        while (currentNode != null || !list.isEmpty()){
            while (currentNode != null){
                list.push(currentNode);
                currentNode = currentNode.leftNode;
            }
            Node self = list.pop();
            System.out.println(self.getValue());
            currentNode = self.rightNode;
        }

    }
    /**
     * 中序遍历,迭代实现
     * @param node
     */
    public void recursiveMid(Node node){
        if (node == null){
            return;
        }
        recursiveMid(node.leftNode);
        System.out.println(node.getValue());
        recursiveMid(node.rightNode);
    }

后序遍历

左,右,中

/**
     * 普通后序
     * 遍历
     * 不使用栈操作
     * @param node
     */
    public void commonAfter(Node node){
        Node virtualNode = new Node(-1);
        virtualNode.leftNode = node;
        Node cur = virtualNode;
        Node pre ;
        while (cur != null){
            if (cur.leftNode == null){
                cur = cur.rightNode;
            }else {
                pre = cur.leftNode;
                while (pre.rightNode != null && pre.rightNode != cur){
                    pre = pre.rightNode;
                }
                if (pre.rightNode == null){
                    pre.rightNode = cur;
                    cur = cur.leftNode;
                }else {
                    pre.rightNode = null;
                    reverseVisit(cur.leftNode,pre);
                    cur = cur.rightNode;
                }

            }
        }
    }

    private void reverseVisit(Node from, Node to) {
        if (from == to){
            System.out.println(from.getValue());
            return;
        }
        reverseVisit(from.rightNode,to);
        System.out.println(from.getValue());
    }
    
     /**
     * 遍历实现 后序
     * @param root
     */
    public void traverseAfter(Node root){
        LinkedList<Node> list = new LinkedList<>();
        //1 标识仅访问了左节点,2标识左右节点都已经访问了
        LinkedList<Integer> visitedState = new LinkedList<>();
        Node currentNode = root;
        //从根开始,一只先访问左子树,再访问右子树,再访问父节点,退出某一层级时当前栈顶则为根节点
        while (currentNode != null || !list.isEmpty()){
            while (currentNode != null){
                //一层层遍历左节点
                list.push(currentNode);
                currentNode = currentNode.leftNode;
                visitedState.push(1);
            }
            while (!list.isEmpty() && visitedState.peek() ==2){
                visitedState.pop();
                System.out.println(list.pop().getValue());
            }
            if (!list.isEmpty()){
                currentNode = list.peek().rightNode;
                visitedState.pop();
                visitedState.push(2);
            }
        }
    }
        /**
     * 后序遍历,迭代实现
     * @param node
     */
    public void recursiveAfter(Node node){
        if (node == null){
            return;
        }
        recursiveAfter(node.leftNode);
        recursiveAfter(node.rightNode);
        System.out.println(node.getValue());
    }

二叉树层序遍历

即广度优先

/**
     * 层序遍历
     * 广度优先遍历
     * @param root
     */
    public void traverseLevel(Node root){
        if (root == null){
            return;
        }
        LinkedList<Node> list = new LinkedList<>();
        list.offer(root);
        while (!list.isEmpty()){
            Node tmp = list.poll();
            System.out.println(tmp.getValue());
            if (tmp.leftNode != null){
                list.offer(tmp.leftNode);
            }
            if (tmp.rightNode != null){
                list.offer(tmp.rightNode);
            }
        }
    }

求树的节点数

 /**
     * 求树的节点数
     * @param root
     * @return
     */
    public int countNode(Node root){
        if (root == null){
            return 0;
        }
        int leftTreeCount = countNode(root.leftNode);
        int rightTreeCount = countNode(root.rightNode);
        return leftTreeCount + rightTreeCount + 1;
    }

求树的叶子节点数

/**
     * 求树的叶子节点数
     * @param root
     * @return
     */
    public int countLeafNode(Node root){
        if (root == null){
            return 0;
        }
        if (root.leftNode == null && root.rightNode == null){
            return 1;
        }else {
            int leftTreeCount = countLeafNode(root.leftNode);
            int rightTreeCount = countLeafNode(root.rightNode);
            return  leftTreeCount + rightTreeCount;
        }
    }

求树高度

/**
     * 求树高度
     * @param root
     * @return
     */
    public int height(Node root){
        if (root == null){
            return 0;
        }
        int leftTreeHeight = height(root.leftNode);
        int rightTreeHeight = height(root.rightNode);
        return leftTreeHeight > rightTreeHeight?leftTreeHeight+1:rightTreeHeight+1;
    }

求第K层的节点数目

/**
     * 求第K层的节点数目
     * @param root
     * @param k
     * @return
     */
    public int countLevelNode(Node root,int k){
        if (root == null || k<1){
            return 0;
        }
        if (k == 1){
            return 1;
        }
        return countLevelNode(root.leftNode,k-1) + countLevelNode(root.rightNode,k-1);
    }

比较两个二叉树结构是否相同

/**
     * 比较两个二叉树结构是否相同
     * @param node1
     * @param node2
     * @return
     */
    public boolean equalStructure(Node node1,Node node2){
        if (node1 == null && node2 == null){
            return true;
        }
        if (node1 == null || node2 == null){
            return false;
        }
        boolean left = equalStructure(node1.leftNode,node2.leftNode);
        boolean right = equalStructure(node1.rightNode,node2.rightNode);
        return left && right;
    }

对树进行镜像(翻转)

 /**
     * 对树进行镜像
     * 对于每个节点,交换它的左右孩子
     * @param root
     */
    public void mirror(Node root){
        if (root == null){
            return;
        }
        Node tmp = root.leftNode;
        root.leftNode = root.rightNode;
        root.rightNode = tmp;
        mirror(root.leftNode);
        mirror(root.rightNode);
    }

求最低公共祖先

 /**
     * 求最低公共祖先
     * @param root
     * @param node1
     * @param node2
     * @return
     */
    public Node lowestCommonAncestor(Node root,Node node1,Node node2){
        if (root == null){
            return null;
        }
        if (node1 == root || node2 == root){
            return root;
        }
        Node left = lowestCommonAncestor(root.leftNode,node1,node2);
        Node right = lowestCommonAncestor(root.rightNode,node1,node2);
        if (left != null && right != null){
            return root;
        }
        return left == null ? right:left;
    }

求两个节点的最短距离

 /**
     * 求两个节点的最短距离
     * @param node1
     * @param node2
     * @return
     */
    public int distance(Node root,Node node1,Node node2){
        Node nodeLCA = lowestCommonAncestor(root,node1,node2);
        return nodeLevel(nodeLCA,node1) + nodeLevel(nodeLCA,node2);
    }

    /**
     * 计算子节点所在层级
     * @param root
     * @param node
     * @return
     */
    private int nodeLevel(Node root, Node node) {
        if (root == null){
            return -1;
        }
        if (root == node){
            return 0;
        }
        int level = nodeLevel(root.leftNode,node);
        if (level == -1){
            level = nodeLevel(root.rightNode,node);
            if (level == -1){
                return level;
            }
        }
        return level +1;
    }

查找所有祖先

/**
     * 查找所有祖先节点
     * @param root
     * @param node
     */
    public boolean allAncestor(Node root,Node node){
        if (root == null){
            return false;
        }
        if (root == node){
            return true;
        }
        if (allAncestor(root.leftNode,node) || allAncestor(root.rightNode,node)){
            System.out.println(root.getValue());
            return true;
        }
        return false;
    }

判断是否为完全二叉树

/**
     * 判断是否为完全二叉树
     * 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,
     * 第 h 层所有的结点都连续集中在最左边,这就是完全二叉树(Complete Binary Tree)
     * @param root
     * @return
     */
    public boolean isPerfect(Node root){
        if (root == null){
            return true;
        }
        boolean border = false;
        Node tmp;
        LinkedList<Node> list = new LinkedList<>();
        list.push(root);
        while (!list.isEmpty()){
            tmp = list.pop();
            if (tmp.leftNode == null){
                if (tmp.rightNode != null){
                    return false;
                }
            }else {
                if (border){
                    return false;
                }
                list.push(tmp.leftNode);
                if (tmp.rightNode == null){
                    border = true;
                }else {
                    list.push(tmp.rightNode);
                }
            }
        }
        return true;
    }

什么是平衡二叉树,什么是红黑二叉树

平衡二叉树定义(AVL):它或者是一颗空树,或者具有以下性质的二叉树:它的左子树和右子树的深度之差(平衡因子)的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树

红黑树,本质上来说就是一棵二叉查找树,但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。红黑树有如下5条性质:

1)每个结点要么是红的,要么是黑的。  
2)根结点是黑的。  
3)每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的。  
4)如果一个结点是红的,那么它的俩个儿子都是黑的。  
5)对于任一结点而言,其到叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点

链表

单向链表结构

只可向一端遍历

public class SingleLinkDemo {
    private int size;
    private Node first;
    private Node last;
	//省略getter 和 setter
    public void addTail(Integer value){
        Node node = new Node(value);
        if(size==0){
            first = node;
            last = node;
        }else{
            last.setNext(node);
            last = node;
        }
        size++;
    }

    public void addHead(Integer value) {
        Node node = new Node(value);
        if(size==0){
            first = node;
            last = node;
        }else{
            node.setNext(first);
            first = node;
        }
        size++;
    }

    public void add(int index,Integer value){
        if (index <size){
            if (size == 0){
                Node node = new Node(value);
                first = node;
                last = node;
                size++;
            }else if (index == 0){
                addHead(value);
            }else if (index ==size -1){
                addTail(value);
            }else{
                Node node = new Node(value);
                Node tmp = getNode(index);
                node.setNext(tmp.getNext());
                tmp.setNext(node);
                size++;
            }
        }else {
            throw new IndexOutOfBoundsException("插入位置无效或超出链表长度");
        }

    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        Node node = first;
        while (node != null){
            System.out.println(node.getValue());
            node = node.getNext();
        }
        return stringBuilder.toString();
    }

    public Node getNode(int index) {
        if (index > size -1){
            throw new IndexOutOfBoundsException("超出链表长度");
        }
        Node node = first;
        for (int i=0;i<index;i++){
            node = node.getNext();
        }
        return node;
    }

    static class Node {
        private Integer value;
        private Node next;
        //省略getter 和setter
    }
}

单向链表的逆序排列

/**
 * 反转链表
 */
public void reverseLink(){
    Node tmp = first;
    last = tmp;
    Node next = first.getNext();
    for (int i = 0;i<size-1;i++){
        Node nextNode = next.getNext();
        next.setNext(tmp);
        tmp = next;
        next = nextNode;
    }
    last.setNext(null);
    first = tmp;
}

双向链表的结构

可以向两端遍历

public class DoubleLinkDemo {

    private int size = 0;
    private Node first;
    private Node last;
	//省略getter 和 setter
    class Node{
        private int value;
        private Node pre;
        private Node next;
        //省略getter 和 setter
    }
}

双向链表的操作

public void addLast(Integer value){
    Node node = new Node(value);
    if(size==0){
        first = node;
        last = node;
    }else{
        node.setPre(last);
        last.setNext(node);
        last = node;
    }
    size++;
}

public void addFirst(Integer value) {
    Node node = new Node(value);
    if(size==0){
        first = node;
        last = node;
    }else{
        first.setPre(node);
        node.setNext(first);
        first = node;
    }
    size++;
}

public void add(int index,Integer value){
    if (index <size){
        if (size == 0){
            Node node = new Node(value);
            first = node;
            last = node;
            size++;
        }else if (index == 0){
            addFirst(value);
        }else if (index ==size -1){
            addLast(value);
        }else{
            Node node = new Node(value);
            Node tmp = getNode(index);
            node.setPre(tmp);
            node.setNext(tmp.getNext());
            tmp.setNext(node);
            size++;
        }
    }else {
        throw new IndexOutOfBoundsException("插入位置无效或超出链表长度");
    }

}

@Override
public String toString() {
    StringBuilder stringBuilder = new StringBuilder();
    Node node = first;
    while (node != null){
        System.out.println(node.getValue());
        node = node.getNext();
    }
    return stringBuilder.toString();
}

public Node getNode(int index) {
    if (index > size -1){
        throw new IndexOutOfBoundsException("超出链表长度");
    }
    Node node = first;
    for (int i=0;i<index;i++){
        node = node.getNext();
    }
    return node;
}

数组

冒泡排序

一直比较取最大的放在最后

/**
 * 冒泡排序
 * 一直比较取最大的放在最后
 * @param ts
 */
public static void bubbingSort(Integer[] ts){
    if (ts.length == 1){
        return;
    }
    int tmp = 0;
    for (int i = 0;i<ts.length;i++){
        for (int j = 0;j<ts.length-1-i;j++){
            if (ts[j]>ts[j+1]){
                tmp = ts[j];
                ts[j] = ts[j+1];
                ts[j+1] = tmp;
            }
        }
    }
}

快速排序

在数据集之中,选择一个元素作为”基准”(pivot)。所有小于”基准”的元素,都移到”基准”的左边;所有大于”基准”的元素,都移到”基准”的右边。这个操作称为分区 (partition) 操作,分区操作结束后,基准元素所处的位置就是最终排序后它的位置。对”基准”左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止

/**
 * 快速排序
 * 在数据集之中,选择一个元素作为”基准”(pivot)。
 * 所有小于”基准”的元素,都移到”基准”的左边;所有大于”基准”的元素,都移到”基准”的右边。这个操作称为分区 (partition) 操作,
 *      分区操作结束后,基准元素所处的位置就是最终排序后它的位置。
 * 对”基准”左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
 * @param ts
 */
public static void fastSort(Integer[] ts){
    if (ts.length>0){
        fastStart(ts,0,ts.length -1);
    }
}

/**
 * 内部递归
 * @param ts
 * @param beginIndex
 * @param endIndex
 */
private static void fastStart(Integer[] ts, int beginIndex, int endIndex) {
    if (beginIndex < endIndex){
        int midIndex = getMidIndex(ts,beginIndex,endIndex);
        fastStart(ts,beginIndex,midIndex-1);
        fastStart(ts,midIndex +1,endIndex);
    }
}

/**
 * 左右移动
 * @param ts
 * @param beginIndex
 * @param endIndex
 * @return
 */
private static int getMidIndex(Integer[] ts, int beginIndex, int endIndex) {
    int tmp = ts[beginIndex];
    while (beginIndex < endIndex){
        //从后开始找,直到找到比基准数值小的
        while (ts[endIndex] >= tmp && beginIndex < endIndex){
            endIndex--;
        }
        //小的往前移动,这时候后边出现了一个重复了,需要有一个大的去填补
        ts[beginIndex] = ts[endIndex];
        //从前边开始找,直到找到比基准数值大的
        while (ts[beginIndex] <tmp && beginIndex < endIndex){
            beginIndex++ ;
        }
        //大的往后移动,填补之前的空缺
        ts[endIndex] = ts[beginIndex];
    }
    //最终,把基准值放在中间
    ts[beginIndex] = tmp;
    return beginIndex;
}

选择排序

/**
 * 选择排序
 * 在未排序序列中找到最小元素,存放到排序序列的起始位置
 * @param ts
 */
public static void selectSort(Integer[] ts){
    if (ts.length <=1){
        return;
    }
    int tmp = 0;
    for (int i =0;i<ts.length;i++){
        //当前最小的值所在索引
        int k = i;
        //找到剩余列表中,最小的
        for (int j = ts.length -1 ;j>i;j--){
            if (ts[j]<ts[k]){
                k = j;
            }
        }
        //把最小的放前面
        tmp = ts[i];
        ts[i] = ts[k];
        ts[k] = tmp;
    }
}

插入排序

/**
 * 插入排序
 * 从第一个元素开始,该元素可以认为已经被排序
 * 取出下一个元素,在已经排序的元素序列中从后向前扫描
 * 如果前一个比当前值大,则把前一个往后移一位,直到不大的时候,此时,当前值应该放在此位置
 * @param ts
 */
public static void insertionSort(Integer[] ts){
    if (ts.length <=1){
        return;
    }
    int tmp = 0;
    int j =0;
    for (int i = 0;i<ts.length;i++){
        tmp = ts[i];
        for (j = i;j>0 && tmp <ts[j-1];j--){
            ts[j] = ts[j-1];
        }
        ts[j] = tmp;
    }
}

希尔排序

/**
 * 希尔排序
 * 先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,
 * 然后依次缩减增量再进行排序,
 * 待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序
 * @param ts
 */
public static void hillSort(Integer[] ts){
    int j = 0;
    int tmp = 0;
    for (int increment = ts.length/2;increment>0;increment/=2){
        for (int i = increment;i<ts.length;i++){
            tmp = ts[i];
            for (j = i;j>=increment;j-=increment){
                if (tmp<ts[j-increment]){
                    ts[j] = ts[j-increment];
                }else {
                    break;
                }
            }
            ts[j] = tmp;
        }
    }
}

归并排序

/**
 * 归并排序
 * 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,
 * 即把待排序序列分为若干个子序列,每个子序列是有序的。
 * 然后再把有序子序列合并为整体有序序列
 * @param ts
 */
public static void mergeSort(Integer[] ts,int beginIndex,int endIndex){
    int midIndex = (beginIndex + endIndex)/2;
    if (beginIndex < endIndex){
        mergeSort(ts,0,midIndex);
        mergeSort(ts,midIndex +1,endIndex);
        merge(ts,beginIndex,midIndex,endIndex);
    }
}

private static void merge(Integer[] ts, int beginIndex, int midIndex, int endIndex) {
    int[] tmp = new int[endIndex-beginIndex +1];
    int i = beginIndex;
    int j = midIndex +1;
    int k =0;
    //把左右中最小的依次放在tmp 中,直到某一边被移动完
    while (i<=midIndex && j<=endIndex){
        if (ts[i] < ts[j]){
            tmp[k++] = ts[i++];
        }else {
            tmp[k++] = ts[j++];
        }
    }
    //把左边剩余的依次放到tmp
    while (i<=midIndex){
        tmp[k++] = ts[i++];
    }
    //把右边剩余的依次放到tmp
    while (j<=endIndex){
        tmp[k++] = ts[j++];
    }
    //替换到原数组中
    for (int m=0;m<tmp.length;m++){
        ts[m + beginIndex] = tmp[m];
    }
}

堆排序

/**
 * 堆排序
 * 将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点。
 * 将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),
 * 然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次最大值。
 * 如此反复执行,就能得到一个有序序列了
 * @param ts
 */
public static void heapSort(Integer[] ts){
    for (int i=ts.length/2;i>=0;i--){
        heapAdjust(ts,i,ts.length);
    }
    for (int j = ts.length -1 ;j>0;j--){
        int tmp = ts[0];
        ts[0] = ts[j];
        ts[j] = tmp;
        heapAdjust(ts,0,j);
    }

}

private static void heapAdjust(Integer[] ts, int index, int length) {
    int child = 0;
    int father = 0;
    for (father = ts[index];leftChild(index)<length;index=child){
        child = leftChild(index);
        if (child != length -1 && ts[child] < ts[child +1]){
            child++;
        }
        if (father <ts[child]){
            ts[index] = ts[child];
        }else {
            break;
        }
    }
    ts[index] = father;
}

private static int leftChild(int index) {
    return 2 * index + 1;
}

数组求逆序对

在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数

/**
 * 逆序
 * @param ts
 */
public static void reverseSelf(Integer[] ts){
    for (int i = 0,j=ts.length-1;i<j;i++,j--){
        int tmp = ts[i];
        ts[i] = ts[j];
        ts[j] = tmp;
    }
}

/**
 * 使用JDK工具类
 * @param ts
 */
public static void reverseTools(Integer[] ts){
    List list = Arrays.asList(ts);
    Collections.reverse(list);
    ts = (Integer[]) list.toArray();
}

整形数组的逆序数

/**
 * 求int数组逆序数
 * 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。
 * 逆序数可如下计算:标出每个数右面比它小的数的个数,它们的和就是逆序数.
 * 例如求436512的逆序数:t(436512)=3+2+3+2+0+0=10.一个排列中逆序的总数就称为这个排列的逆序数。
 * 逆序数为偶数的排列称为偶排列;
 * 逆序数为奇数的排列称为奇排列。如2431中,21,43,41,31是逆序,逆序数是4,为偶排列
 * 使用归并排序
 */
public static int inverseCount(Integer[] ts,int beginIndex,int endIndex){
    int midIndex = (beginIndex + endIndex)/2;
    if (beginIndex < endIndex){
        int left = inverseCount(ts,0,midIndex);
        int right = inverseCount(ts,midIndex +1,endIndex);
        int count = mergeCount(ts,beginIndex,midIndex,endIndex);
        return left + right + count;
    }
    return 0;
}

private static int mergeCount(Integer[] ts, int beginIndex, int midIndex, int endIndex) {
    int[] tmp = new int[endIndex-beginIndex +1];
    int i = beginIndex;
    int j = midIndex +1;
    int k =0;
    int count = 0;
    //把左右中最小的依次放在tmp 中,直到某一边被移动完
    while (i<=midIndex && j<=endIndex){
        if (ts[i] <= ts[j]){
            tmp[k++] = ts[i++];
        }else {
            //如果前面的大于后面的,则前面剩余的值都大于后边当前值,则逆序数需要加上之前的全部数量
            count += j-i;
            for (int m = i;m<=midIndex;m++){
                System.out.println(ts[m] + "-"+ts[j]);
            }
            tmp[k++] = ts[j++];
        }
    }
    //把左边剩余的依次放到tmp
    while (i<=midIndex){
        tmp[k++] = ts[i++];
    }
    //把右边剩余的依次放到tmp
    while (j<=endIndex){
        tmp[k++] = ts[j++];
    }
    //替换到原数组中
    for (int m=0;m<tmp.length;m++){
        ts[m + beginIndex] = tmp[m];
    }
    return count;
}

二分查找的算法

/**
 * 递归二分查找
 * @param ts
 * @param key
 * @param beginIndex
 * @param endIndex
 * @return
 */
public static Integer binarySearch(Integer[] ts,int key,int beginIndex,int endIndex){
    if (beginIndex == endIndex){
        if (ts[beginIndex] == key){
            return beginIndex;
        }else {
            return null;
        }
    }else{
        int midIndex = (beginIndex + endIndex) /2;
        if (ts[midIndex] == key){
            return midIndex;
        }
        if (ts[midIndex] < key){
           return binarySearch(ts,key,midIndex +1,endIndex);
        }else {
            return binarySearch(ts,key,beginIndex,midIndex);
        }
    }
}

/**
 * 非递归实现二分查找
 * @param ts
 * @param key
 * @return
 */
public static Integer commonBinarySearch(Integer[] ts,int key){
    int beginIndex = 0;
    int endIndex = ts.length -1;
    int midIndex ;
    while (beginIndex <= endIndex){
        midIndex = (endIndex + beginIndex) /2;
        if (midIndex == beginIndex){
            if (ts[midIndex] == key){
                return midIndex;
            }else {
                break;
            }
        }else {
            if (ts[midIndex] < key){
                beginIndex = midIndex + 1;
            }else {
                endIndex = midIndex;
            }
        }
    }
    return null;
}

其他

1个整数的倒序输出

循环求余

2.5亿个整数的倒序输出(不重复)

使用bitmap,bitmap 是使用0,1 来标识对应值是否存在以及存在次数,例如 ,0 标识不存在,1标识存在,或者00标识不存在,01 标识出现一次,10标识出现多次。

给定2.5亿个整数(无符号),如果直接加载这2.5亿个整数,则需要2.5亿 * 4 * 8 bit ,而我们使用bitmap 则仅仅需要2.5亿个bit 即可大概是512M,我们分段遍历这2.5亿个整数,如果存在则在对应的bi位上设为1,默认为0,然后逆序遍历整个bit数组,然后输出对应位上的值。

一个文件,有45亿个阿拉伯数字,如何进行去重啊如何找出最大的那个数

分治 + bitmap ,分治 + 最大/最小堆

给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中

可以用位图/Bitmap的方法,申请512M的内存,一个bit位代表一个unsigned int值。读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在

2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5 亿个整数

将bit-map 扩展一下,用2bit 表示一个数即可,00 表示未出现,01 表示出现一次,10 表示出现2 次及以上,在遍历这些数的时候,如果对应位置的值是0 ,则将其置为01 ;如果是01 ,将其置为10 ;如果是10 ,则保持不变。或者我们不用2bit 来进行表示,我们用两个bit-map 即可模拟实现这个2bit-map ,都是一样的道理

10亿个正整数找出重复次数最多的100个整数

分治 + hashMap/分治 + 最大/最小堆

找出给定字符串中最长回文

如果一个字符串正着读和反着读是一样的,那它就是回文串

方法一,遍历所有子串

方法二,中轴查找,我们知道整个字符串中的所有字符,以及字符间的空隙,都可能是某个回文子串的对称轴位置。可以遍历这些位置,在每个位置上同时向左和向右扩展,直到左右两边的字符不同,或者达到边界。

方法三,Manacher 算法

/**
     * manacher算法求取最大回文
     * 算法思想
     * 1 把偶数、奇数长的字符序列变成奇数长度 (所有的空隙位置(包括首尾)插入同样的符号)
     * 2 创建一个与字符串等长的数组,用来记录字符序列相应位置上字符的最长回文半径,半径为1时默认为字符本身
     * 3 然后以每个字符为中轴遍历字符序列,之后求数组的最大值即为最大的半径,即为最长的回文半径
     * @param s
     */
    public static void doManacher(String s) {
        //在字符串两头和质检添加特殊字符转成奇数长度,原理:奇数+奇数+1=奇数,偶数+偶数+1=奇数。
        StringBuffer sb = new StringBuffer();
        sb.append("#");
        for (int i = 0; i < s.length(); i++) {
            sb = sb.append(s.substring(i, i + 1)).append("#");
        }
        s = sb.toString();

//    以每个字符为轴求最长回文串半径,其中半径=1表示字符本身。
        int[] p = new int[s.length()];
        int left, right = 0;
        for (int i = 0; i < s.length(); i++) {
            int len = 1;
            for (left = i - 1, right = i + 1; left >= 0 && right <= (2 * i) && right < s.length(); left--, right++) {
                if (s.charAt(left) == s.charAt(right)) {
                    len = len + 1;
                    continue;//如果匹配成功就继续
                } else {
                    break;//不成功就跳出循环
                }
            }
            p[i] = len;
        }//end wai for

        //求最大的p[i]值
        int pos, maxValuePos = 0;
        for (int i = 0; i < p.length - 1; i++) {
            pos = i;
            for (int j = i + 1; j < p.length; j++) {
                if (p[i] < p[j]) {
                    pos = j;
                    int tep = p[i];
                    p[i] = p[pos];
                    p[pos] = tep;
                }
                if (i == 0){
                    maxValuePos = pos;
                }
            }
        }
        //求得的回文串一定是奇数长度
        int realLen = ((p[0] * 2 - 1) - 1) / 2;//最长回文串的长度,去掉其他字符
        System.out.println("最长的回文串长度为:" + realLen);


        //求最长回文串内容
        String huiwen;
        StringBuffer realHuiwen = new StringBuffer();
        if (realLen == 1) {
            System.out.println("最长回文串为:" + s.charAt(maxValuePos));
        } else {
//              截出来
            huiwen = s.substring((maxValuePos + 1 - p[0]), maxValuePos + p[0]);
//             去掉辅助字符
            for (int j = 0; j < huiwen.length(); j++) {
                if (j % 2 != 0){
                    realHuiwen = realHuiwen.append(huiwen.charAt(j));
                }
            }
            System.out.println("最长回文串为:" + realHuiwen.toString());
        }

    }

    /**
     *  最长回文
     *  中心扩展
     *  因为回文字符串是以中心轴对称的,所以如果我们从下标 i 出发,用2个指针向 i 的两边扩展判断是否相等,
     *  那么只需要对0到n-1的下标都做此操作,就可以求出最长的回文子串。
     *  但需要注意的是,回文字符串有奇偶对称之分,即”abcba”与”abba”2种类型,因此需要在代码编写时都做判断
     * @param s
     * @return
     */
    public  static String longPalindrome2(String s) {
        if (s.isEmpty()) {
            return null;
        }
        if (s.length() == 1) {
            return s;
        }
        String longest = s.substring(0, 1);
        for (int i = 0; i < s.length(); i++) {
            // get longest palindrome with center of i
            String tmp = helper(s, i, i);
            if (tmp.length() > longest.length()) {
                longest = tmp;
            }

            // get longest palindrome with center of i, i+1
            tmp = helper(s, i, i + 1);
            if (tmp.length() > longest.length()) {
                longest = tmp;
            }
        }
        return longest;
    }

    public static String helper(String s, int begin, int end) {
        while (begin >= 0 && end <= s.length() - 1
                && s.charAt(begin) == s.charAt(end)) {
            begin--;
            end++;
        }
        String subS = s.substring(begin + 1, end);
        return subS;
    }

    /**
     * 最长回文
     * 动态规划
     * 从左开始遍历,然后不断的从原字符串中拿出1到length-1长度的字串,进行判断
     * 这里用一个二维数组来表示回文字符串的起始位置和结束位置,值1代表的是回文字符串,0代表不是
     * @param s
     * @return
     */
    public static String longPalindrome(String s) {
        if (s == null){
            return null;
        }
        if(s.length() <=1){
            return s;
        }
        int maxLen = 0;
        String longestStr = null;
        int length = s.length();
        int[][] table = new int[length][length];
        //every single letter is palindrome
        for (int i = 0; i < length; i++) {
            table[i][i] = 1;
        }
        printTable(table);
        //two consecutive same letters are palindrome
        for (int i = 0; i <= length - 2; i++) {
            if (s.charAt(i) == s.charAt(i + 1)){
                table[i][i + 1] = 1;
                longestStr = s.substring(i, i + 2);
            }
        }
        System.out.println(longestStr);
        printTable(table);
        //condition for calculate whole table
        for (int l = 3; l <= length; l++) {
            for (int i = 0; i <= length-l; i++) {
                int j = i + l - 1;
                if (s.charAt(i) == s.charAt(j)) {
                    table[i][j] = table[i + 1][j - 1];
                    if (table[i][j] == 1 && l > maxLen) {
                        longestStr = s.substring(i, j + 1);
                    }
                } else {
                    table[i][j] = 0;
                }
                printTable(table);
            }
        }
        return longestStr;
    }
    public static void printTable(int[][] x){
        for(int [] y : x){
            for(int z: y){
                System.out.print(z + " ");
            }
            System.out.println();
        }
        System.out.println("------");
    }

求字符串中指定字符出现的次数

public int indexOf(int ch, int fromIndex) 返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索,每搜索一次,下次搜索就从这个索引开始往后搜索

Java 基础核心总结》 Java 概述 什么是 Java2 Java 的特点Java 开发环境 JDK JRE Java 开发环境配置 Java 基本语法 数据类型基础语法运算符 Java 执行控制流程条件语句 if 条件语句 if...else 条件语句if...else if 多分支语句switch 多分支语句 循环语句 while 循环语句do...while 循环for 循环语句 跳转语句 break 语句 continue 语句面向对象 类也是-种对象对象的创建 属性和方法 构造方法 方法重载 方法的重写 初始化 类的初始化 成员初始化 构造器初始化初始化顺序 数组初始化 对象的销毁 对象作用域 this 和 super 访问控制权限继承 多态组合代理 向上转型static final 接口和抽象类接口 抽象类异常 认 识 Exception 什么是 Throwable 常见的 Exception 与 Exception 有关的 Java 关键字 throws 和 throw try 、finally 、catch 什么是 Error 内部类 创建内部类集合 Iterable 接口顶层接口 ArrayList Vector LinkedList 类Stack HashSet TreeSet LinkedHashSet 类 PriorityQueue HashMap TreeMap 类 LinkedHashMap 类 Hashtable 类IdentityHashMap 类WeakHashMap 类 Collections 类集合实现类特征图 泛形 泛型的使用 用泛型表示类 用泛型表示接口泛型方法 泛型通配符 反射 Class 类Field 类Method 类ClassLoader 类 枚举 枚举特性 枚举和普通类-样枚举神秘之处 枚举类 I/O File 类 基础 IO 类和相关方法InputStream OutputStream Reader 类Writer 类 InputStream 及其子类 OutputStream 及其子类Reader 及其子类Writer 及其子类 注解 关于 null 的几种处理方式大小写敏感 null 是任何引用类型的初始值 null 只是-种特殊的值使用 Null-Safe 方法null 判断 关于思维导图 Java.IO Java.lang Java.math Java.net Java 基础核心总结 V2.0 IO 传统的 BIO BIO NIO 和 AIO 的区别什么是流 流的分类 节点流和处理流 Java IO 的核心类 File Java IO 流对象 字节流对象InputStream OutputStream 字符流对象Reader Writer 字节流与字符流的转换新潮的 NIO 缓冲区(Buffer)通道(Channel) 示例:文件拷贝案例 BIO 和 NIO 拷贝文件的区别操作系统的零拷贝 选择器(Selectors) 选择键(SelectionKey) 示例:简易的客户端服务器通信 集合 集合框架总览 -、Iterator Iterable ListIterator 二、Map 和 Collection 接口Map 集合体系详解 HashMap LinkedHashMap TreeMap WeakHashMap Hashtable Collection 集合体系详解 Set 接口 AbstractSet 抽象类SortedSet 接口HashSet LinkedHashSet TreeSet List 接口 AbstractList 和 AbstractSequentialList Vector Stack ArrayList LinkedList Queue接口Deque 接口 AbstractQueue 抽象类LinkedList ArrayDeque PriorityQueue 反射的思想及作用 反射的基本使用 获取类的 Class 对象构造类的实例化对象获取-个类的所有信息 获取类中的变量(Field) 获取类中的方法(Method) 获取类的构造器(Constructor) 获取注解 通过反射调用方法反射的应用场景 Spring 的 IOC 容器反射 + 抽象工厂模式 JDBC 加载数据库驱动类反射的优势及缺陷 增加程序的灵活性破坏类的封装性 性能损耗 代理模式 静态代理与动态代理常见的动态代理实现JDK Proxy CGLIB JDK Proxy 和 CGLIB 的对比动态代理的实际应用 Spring AOP 变量 变量汇总实例变量 实例变量的特点全局变量 静态变量 静态变量的特点类变量 局部变量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值