sword20

  1. 单例模式

public class No2 {

    /**
     * 设计一个类,我们只能生成该类的一个实例。
     */
    public static void main(String[] args) {

    }

}

//饿汉式  线程安全
class A {
    private static final A a = new A();
    private A() {
    }

    public static A getInstance() {
        return a;
    }
}

//懒汉式 线程安全写法
class B {
    private static B b = null;
    private B() {
    }

    public static B getInstance() {
        if (b == null) {
            synchronized (B.class) {
                if (b == null)
                    b = new B();
            }
        }
        return b;
    }
}

//真正的线程安全是加上volatile,防止初始化对象和instance指向内存两个步骤重排序
class C {
    private volatile static C instance;
    public static C getInstance() {
        if (instance == null) {
            synchronized (C.class) {
                if (instance == null) instance = new C(); // instance为volatile,现在没问题了
            }
        }
        return instance;
    }
}

//静态内部类的方法
class InstanceFactory {
    private static class InstanceHolder {
        public static C instance = new C();
    }

    public static C getInstance() {
        return InstanceHolder.instance;//这里将导致InstanceHolder类被初始化
    }
}
  1. 数组中重复的数字

/**
 * Created by ryder on 2017/6/11. * 一个长度为n的数组,值的范围在0~n-1内,有一个或多个数字重复,求其中任意一个
 */

public class P39_DuplicationInArray {

    //方法一:暴力求解,不会修改原始数据,时间复杂度o(n^2),空间复杂度o(1)
    public static int getDuplication(int[] data) {
        if (data == null || data.length < 2) return -1;
        for (int i = 0; i < data.length - 1; i++) {
            for (int j = i + 1; j < data.length; j++) {
                if (data[i] == data[j]) return data[i];
            }
        }
        return -1;
    }

    //方法二:排序,会修改原始数据,时间复杂度o(nlogn),空间复杂度o(1)
    public static int getDuplication2(int[] data) {
        if (data == null || data.length < 2)
            return -1; //Arrays.sort(data); //或者使用内置函数进行排序

        quickSort(data, 0, data.length - 1);
        if (data.length < 2) return -1;
        int prev = data[0];
        for (int i = 1; i < data.length; i++) {
            if (data[i] == prev) return prev;
            else prev = data[i];
        }
        return -1;
    }

    public static void quickSort(int[] data, int start, int end) {
        if (start >= end) return;
        int bound = partion(data, start, end);
        quickSort(data, start, bound - 1);
        quickSort(data, bound + 1, end);
    }

    public static int partion(int[] data, int start, int end) {
        if (start >= end) return end;
        int pivot = data[start];
        int left = start, right = end;
        while (left < right) {
            while (left < right && data[right] >= pivot) right--;
            if (left < right) data[left++] = data[right];
            while (left < right && data[left] < pivot) left++;
            if (left < right) data[right--] = data[left];
        }
        data[left] = pivot;
        return left;
    } 

    //方法三:借助哈希表,不会修改原始数据,时间复杂度o(n),空间复杂度o(n)
    public static int getDuplication3(int[] data) {
        if (data == null || data.length < 2) return -1;
        int[] hashTable = new int[data.length];
        for (int item : data) {
            if (hashTable[item] >= 1) return item;
            else {
                hashTable[item] = 1;
            }
        }
        return -1;
    }


    //方法四:根据数字特点排序,会修改原始数据,时间复杂度o(n),空间复杂度o(1)
    //数据特点就是范围在0~n-1
    //data[i] != i表示数据不在应该的位置
    //while循环直到把当前位置替换成应该的数字
    public static int getDuplication4(int[] data) {
        if (data == null || data.length < 2) return -1;
        for (int i = 0; i < data.length; i++) {
            while (data[i] != i) {
                if (data[i] == data[data[i]]) return data[i];
                else {
                    int temp = data[i];
                    data[i] = data[temp];
                    data[temp] = temp;
                }
            }
        }
        return -1;
    } 

    //方法五:类似于二路归并,这个思路应该说是二路计数,不修改原始数据,时间复杂度o(nlogn),空间复杂度o(1)
    //数值在0~n-1,也是在[start,end]间
    //[start,middle]间的数字要是超过了middle-start+1,说明[start,middle]间肯定有重复数字
    public static int getDuplication5(int[] data) {
        if (data == null || data.length < 2)
            return -1; 
        //数组值在[start,end]间
        int start = 0;
        int end = data.length - 2;
        while (start <= end) {
            int middle = (end - start) / 2 + start;
            int count = countRange(data, start, middle);
            if (start == end) {
                if (count > 1) return start;
                else return -1;
            }
            if (count > middle - start + 1) end = middle;
            else start = middle + 1;
        }
        return -1;
    }
    //求在这范围的数字有几个
    public static int countRange(int[] data, int start, int end) {
        int count = 0;
        for (int i = 0; i < data.length; i++) {
            if (start <= data[i] && end >= data[i]) count++;
        }
        return count;
    }

    public static void main(String[] args) {
        int[] data = {2, 3, 1, 0, 2, 5, 3};
        System.out.println(getDuplication(data));
        System.out.println(getDuplication2(data));
        System.out.println(getDuplication3(data));
        System.out.println(getDuplication4(data));
        System.out.println(getDuplication5(data));
        int[] data1 = {2, 3, 1, 0, 4, 5, 5};
        System.out.println(getDuplication(data1));
        System.out.println(getDuplication2(data1));
        System.out.println(getDuplication3(data1));
        System.out.println(getDuplication4(data1));
        System.out.println(getDuplication5(data1));
    }
}



  1. 二维数组中的查找
/**
 * Created by ryder on 2017/6/12. * 二维数组,从左到右递增,从上到下递增,输入一个整数,判断数组中是否含有
 */
public class P44_FindInPartiallySortedMatrix {
    public static boolean findInPartiallySortedMatrix(int[][] data, int target) {
        if (data == null || data.length == 0 || data[0].length == 0) return false;
        int rowMax = data.length - 1, colMax = data[0].length - 1;
        int rowCur = data.length - 1, colCur = 0;
        while (true) {
            if (rowCur < 0 | rowCur > rowMax | colCur < 0 | colCur > colMax) return false;
            if (data[rowCur][colCur] == target) return true;
            else if (data[rowCur][colCur] > target) rowCur--;
            else colCur++;
        }
    }

    public static void main(String[] args) {
        int[][] data = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
        System.out.println(findInPartiallySortedMatrix(data, 10));
        System.out.println(findInPartiallySortedMatrix(data, 5));
    }
}

  1. 替换空格
public class P51_ReplaceSpaces {
    //由于java的字符数组没有结束符,所以需要多传入个原始长度 
    // 先计算好替换后的位置,从后向前替换,时间复杂度o(n) 
    public static void replaceBlank(char[] data, int length) {
        int newLength = length;
        for (int i = 0; i < length; i++) {
            if (data[i] == ' ') newLength += 2; 
        }
        for (int indexOfOld = length - 1, indexOfNew = newLength - 1; indexOfOld >= 0 && indexOfOld != indexOfNew; indexOfOld--, indexOfNew--) {
            if (data[indexOfOld] == ' ') {
                data[indexOfNew--] = '0';
                data[indexOfNew--] = '2';
                data[indexOfNew] = '%';
            } else {
                data[indexOfNew] = data[indexOfOld];
            }
        }
    }

    public static void main(String[] args) {
        char[] predata = "We are happy.".toCharArray();
        char[] data = new char[20];
        for (int i = 0; i < predata.length; i++) data[i] = predata[i];
        System.out.println(data);
        replaceBlank(data, 13);
        System.out.println(data);
    }
}

  1. 从尾到头打印链表
public class ListNode {
    public T val;
    public ListNode next;

    public ListNode(T val) {
        this.val = val;
        this.next = null;
    }

    @Override
    public String toString() {
        StringBuilder ret = new StringBuilder();
        ret.append("[");
        for (ListNode cur = this; ; cur = cur.next) {
            if (cur == null) {
                ret.deleteCharAt(ret.lastIndexOf(" "));
                ret.deleteCharAt(ret.lastIndexOf(","));
                break;
            }
            ret.append(cur.val);
            ret.append(", ");
        }
        ret.append("]");
        return ret.toString();
    }
}



/**
 * Created by ryder on 2017/6/13. * 从尾到头打印链表
 */
public class P58_PrintListInReversedOrder { 
    //递归版 
    public static void printReversinglyRecursively(ListNode node) {
        if (node == null) return;
        else {
            printReversinglyRecursively(node.next);
            System.out.println(node.val);
        }
    } 

    //非递归版 
    public static void printReversinglyIteratively(ListNode node) {
        Stack stack = new Stack<>();
        for (ListNode temp = node; temp != null; temp = temp.next) 
            stack.add(temp.val);
        while (!stack.isEmpty()) 
            System.out.println(stack.pop());
    }

    public static void main(String[] args) {
        ListNode head = new ListNode(1);
        head.next = new ListNode(2);
        head.next.next = new ListNode(3);
        printReversinglyRecursively(head);
        System.out.println();
        printReversinglyIteratively(head);
    }
}



二叉树的遍历


/**
 * 二叉树的遍历:先序(递归,非递归),中序(递归,非递归),后序(递归,非递归),层序
 */
public class P60_TraversalOfBinaryTree {

    //先序遍历递归自己写
    public static void preorderRecursively(TreeNode node) {
        System.out.println(node.val);
        if (node.left != null)
            preorderRecursively(node.left);
        if (node.right != null)
            preorderRecursively(node.right);
    }
    
    //先序遍历递归版
    public static List preorderRecursively(TreeNode node) {
        List list = new ArrayList<>();
        if (node == null) return list;
        list.add(node.val);
        list.addAll(preorderRecursively(node.left));
        list.addAll(preorderRecursively(node.right));
        return list;
    }

    //中序遍历递归版
    public static List inorderRecursively(TreeNode node) {
        List list = new ArrayList<>();
        if (node == null) return list;
        list.addAll(inorderRecursively(node.left));
        list.add(node.val);
        list.addAll(inorderRecursively(node.right));
        return list;
    } 

    //后序遍历递归版
    public static List postorderRecursively(TreeNode node) {
        List list = new ArrayList<>();
        if (node == null) return list;
        list.addAll(postorderRecursively(node.left));
        list.addAll(postorderRecursively(node.right));
        list.add(node.val);
        return list;
    }

    //非递归每次当前为空了,就把栈顶出栈,当前结点变为出栈结点右孩子
    //先序是当前节点入栈前,把结果放到list里面
    //中序是出栈之前,把结果放到list里面
    //先序遍历非递归版
    public static List preorderIteratively(TreeNode node) {
        //stack栈顶元素永远为cur的父节点
        Stack> stack = new Stack<>();
        TreeNode cur = node;
        List list = new LinkedList<>();
        if (node == null) return list;
        while (cur != null || !stack.isEmpty()) {
            if (cur != null) {
                list.add(cur.val);
                stack.push(cur);
                cur = cur.left;
            } else {
                cur = stack.pop().right;
            }
        }
        return list;
    }

    //中序遍历非递归版
    public static List inorderIteratively(TreeNode node) {
        //stack栈顶元素永远为cur的父节点
        Stack> stack = new Stack<>();
        TreeNode cur = node;
        List list = new LinkedList<>();
        while (cur != null || !stack.isEmpty()) {
            if (cur != null) {
                stack.push(cur);
                cur = cur.left;
            } else {
                list.add(stack.peek().val);
                cur = stack.pop().right;
            }
        }
        return list;
    }

    //后序遍历非递归版
    public static List postorderIteratively(TreeNode node) {
        //stack栈顶元素永远为cur的父节点
        // prevVisted用于区分是从左子树还是右子树返回的
        Stack> stack = new Stack<>();
        TreeNode cur = node;
        TreeNode prevVisted = null;
        List list = new LinkedList<>();
        while (cur != null || !stack.isEmpty()) {
            if (cur != null) {
                stack.push(cur);
                cur = cur.left;
            } else {                                     //当前为空了,把当前节点变成栈顶的右子结点
                cur = stack.peek().right;

                if (cur != null && cur != prevVisted) { //不为空,且不是刚出栈
                                                        //需要判断当前节点是否刚被弹出栈,否则会多次进栈
                    stack.push(cur);
                    cur = cur.left;
                } else {                                //当前节点还是为空或者刚出过栈,栈顶出栈并放到结果数组
                    prevVisted = stack.pop();
                    list.add(prevVisted.val);
                    cur = null;
                }
            }
        }
        return list;
    }

    //层序遍历
    public static List levelorder(TreeNode node) {
        Queue> queue = new LinkedList<>();
        List list = new LinkedList<>();
        TreeNode temp = null;
        if (node == null) return list;
        queue.add(node);
        while (!queue.isEmpty()) {
            temp = queue.poll();
            list.add(temp.val);
            if (temp.left != null) queue.offer(temp.left);
            if (temp.right != null) queue.offer(temp.right);
        }
        return list;
    }

    public static void main(String[] args) {
        //            1
        //              \
        //               2
        //              /
        //            3
        //pre->123  in->132   post->321  level->123
        TreeNode root = new TreeNode(1);
        root.right = new TreeNode(2);
        root.right.left = new TreeNode(3);
        List list_preorderRecursively = preorderRecursively(root);
        System.out.print("preorderRecursively: " + '\t');
        System.out.println(list_preorderRecursively.toString());
        List list_inorderRecursively = inorderRecursively(root);
        System.out.print("inorderRecursively: " + '\t');
        System.out.println(list_inorderRecursively.toString());
        List list_postorderRecursively = postorderRecursively(root);
        System.out.print("postorderRecursively: " + '\t');
        System.out.println(list_postorderRecursively.toString());
        System.out.println();
        List list_preorderIteratively = preorderIteratively(root);
        System.out.print("preorderIteratively: " + '\t');
        System.out.println(list_preorderIteratively.toString());
        List list_inorderIteratively = inorderIteratively(root);
        System.out.print("inorderIteratively: " + '\t');
        System.out.println(list_inorderIteratively.toString());
        List list_postorderIteratively = postorderIteratively(root);
        System.out.print("postorderIteratively: " + '\t');
        System.out.println(list_postorderIteratively.toString());
        System.out.println();
        List list_levelorder = levelorder(root);
        System.out.print("levelorder: " + '\t');
        System.out.println(list_levelorder.toString());
    }
}
  1. 重建二叉树

/**
 * 重建二叉树:  先序+中序,后续+中序可以完成重建,而先序+后序无法完成
 */
public class P62_ConstructBinaryTree {
    public static TreeNode construct(int[] preorder, int[] inorder) {
        if (preorder == null || inorder == null || preorder.length == 0 || preorder.length != inorder.length)
            return null;
        //递归时需要传递的,先序和中序的开始位置初始为0,length是中序的长度
        return constructCore(preorder, 0, inorder, 0, inorder.length); 
    }

    public static TreeNode constructCore(int[] preorder, int preorder_start, int[] inorder, int inorder_start, int length) {
        //length是先序的长度,为零是递归出口
        if (length == 0) return null; 
        int inorder_index = -1;
        //遍历中序的序列
        for (int i = inorder_start; i < inorder_start + length; i++) {
            //找到当前的根节点位置,记录到inorder_index
            if (inorder[i] == preorder[preorder_start]) { 
                inorder_index = i;
                break;
            }
        }
        //左边中序序列的长度,右边序列长length-left_length-1
        int left_length = inorder_index - inorder_start;
        //根节点变成TreeNode,node的左右孩子是向下递归的结果
        TreeNode node = new TreeNode(preorder[preorder_start]);
        node.left = constructCore(preorder, preorder_start + 1, inorder, inorder_start, left_length);
        node.right = constructCore(preorder, preorder_start + left_length + 1, inorder, inorder_index + 1, length - left_length - 1);
        return node;
    }

    public static void main(String[] args) { 
        //    1 
        //   / \ 
        //  2   3 
        // / \ 
        // 4  5 
        // pre->12453 in->42513 post->45231 

        int[] pre = {1, 2, 4, 5, 3};
        int[] in = {4, 2, 5, 1, 3};
        TreeNode root = construct(pre, in); 
        //对重建后的树,进行前中后序遍历,验证是否重建正确 
        // 调用的重建函数见:http://www.jianshu.com/p/362d4ff42ab2 
        
        List preorder = P60_TraversalOfBinaryTree.preorderIteratively(root);
        List inorder = P60_TraversalOfBinaryTree.inorderIteratively(root);
        List postorder = P60_TraversalOfBinaryTree.postorderIteratively(root);
        System.out.println(preorder);
        System.out.println(inorder);
        System.out.println(postorder);
    }
}

  1. 二叉树的下一个节点

/*
题目要求:
给定二叉树和其中一个节点,找到中序遍历序列的下一个节点。
树中的节点除了有左右孩子指针,还有一个指向父节点的指针。
*/

/*
思路:
(1)如果输入的当前节点有右孩子,则它的下一个节点即为该右孩子为根节点的子树的最左边的节点,比如2->5,1->3
(2)如果输入的当前节点没有右孩子,就需要判断其与自身父节点的关系:
(2.1)如果当前节点没有父节点,那所求的下一个节点不存在,返回null.
(2.2)如果输入节点是他父节点的左孩子,那他的父节点就是所求的下一个节点,比如4->2
(2.3)如果输入节点是他父节点的右孩子,那就需要将输入节点的父节点作为新的当前节点,
 返回到(2),判断新的当前节点与他自身父节点的关系,比如5->1
 */


//带有父指针的二叉树节点
class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
    public TreeNode father;
    public TreeNode(int val){
        this.val = val;
        this.left = null;
        this.right = null;
        this.father = null;
    }
}

public class P65_NextNodeInBinaryTrees {
    public static TreeNode getNext(TreeNode pNode){
        if(pNode==null)
            return null;
        else if(pNode.right!=null){
            pNode = pNode.right;
            while(pNode.left!=null)
                pNode = pNode.left;
            return pNode;
        }
        while(pNode.father!=null){
            if(pNode.father.left==pNode)
                return pNode.father;
            pNode = pNode.father;
        }
        return null;
    }
    public static void main(String[] args){
        //            1
        //          // \\
        //         2     3
        //       // \\
        //      4     5
        //    inorder->42513
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.left.father = root;
        root.right = new TreeNode(3);
        root.right.father = root;
        root.left.left = new TreeNode(4);
        root.left.left.father = root.left;
        root.left.right = new TreeNode(5);
        root.left.right.father = root.left;

        System.out.println(getNext(root.left.left).val);
        System.out.println(getNext(root.left).val);
        System.out.println(getNext(root.left.right).val);
        System.out.println(getNext(root).val);
        System.out.println(getNext(root.right));
    }
}

  1. 用两个栈实现队列

/*
 思路:
(1)对于插入操作,栈与队列都是从队尾进行,因此一行代码就可以完成offer()
(2)对于弹出操作,队列先进先出从队头开始,而栈后进先出从队尾开始,要想取到队头元素,
 就得需要第二个栈stack2的协助:弹出时将stack1的元素依次取出放到stack2中,此时stack2
 进行弹出的顺序就是整个队列的弹出顺序。而如果需要插入,放到stack1中即可。
*/

//stack2有值就2出栈,否则将1导入2,再出栈

class MyQueue{
    private Stack stack1 = new Stack<>();
    private Stack stack2 = new Stack<>();
    
    public void offer(T data){
        stack1.push(data);   
    }
    public T poll(){
        if(!stack2.isEmpty()){
            return stack2.pop();
        }
        else if(!stack1.isEmpty()){
            while(!stack1.isEmpty())
                stack2.push(stack1.pop());
            return stack2.pop();
        }
        else
            return null;
    }
}

public class P68_QueueWithTwoStacks {
    public static void main(String[] args){
        MyQueue myQueue = new MyQueue<>();
        System.out.println(myQueue.poll());
        myQueue.offer(1);
        myQueue.offer(2);
        myQueue.offer(3);
        System.out.println(myQueue.poll());
        System.out.println(myQueue.poll());
        myQueue.offer(4);
        System.out.println(myQueue.poll());
        System.out.println(myQueue.poll());
        System.out.println(myQueue.poll());
    }
}

  1. 斐波那契数列
/**
 * Created by ryder on 2017/6/21.
 * 斐波那契数列
 * f(0)=0,f(1)=1,f(n)=f(n-1)+f(n-2) n>1
 */

解法  解法介绍        时间复杂度   空间复杂度
解法1 依定义递归求解     o(n^2)     o(1)
解法2 从0开始迭代求解    o(n)       o(1)
解法3 借助等比数列公式   o(logn)    o(1)
解法4 借助通项公式       o(1)       o(1)
public class P74_Fibonacci {

    // 依据原始概念的递归解法,时间复杂度o(n^2)
    public static int fibonacci1(int n){
        if(n<=0)
            return 0;
        if(n==1)
            return 1;
        return fibonacci1(n-1)+fibonacci1(n-2);
    }

    // 当前状态只与前两个状态有关。存储前两个值,计算后一个,迭代进行。时间复杂度o(n)
    public static int fibonacci2(int n){
        if(n<=0)
            return 0;
        if(n==1)
            return 1;
        int temp1 =0,temp2=1;
        int result = temp1 + temp2,i=3;
        while(i<=n){
            //也可用一个队列来完成下面三行的操作
            temp1 = temp2;
            temp2 = result;
            result = temp1+temp2;
            i++;
        }
        return result;
    }

    // 借助如下数学公式解决问题。矩阵乘法部分,可用递归解决,时间复杂度o(logn)
    // [ f(n)  f(n-1) ] = [ 1  1 ] ^ n-1   (当n>2)
    // [f(n-1) f(n-2) ]   [ 1  0 ]
    // 证明:
    // [ f(n)  f(n-1) ] = [ f(n-1)+f(n-2)  f(n-1)] = [ f(n-1)  f(n-2)] * [1 1]
    // [f(n-1) f(n-2) ]   [ f(n-2)+f(n-3)  f(n-2)]   [ f(n-2)  f(n-3)]   [1 0]
    // 得到如上递推式,所以
    // [ f(n)  f(n-1) ] = [ f(2)  f(1)] * [1 1]^n-2 = [1 1]^n-1
    // [f(n-1) f(n-2) ]   [ f(1)  f(0)]   [1 0]       [1 0]
    public static int fibonacci3(int n){
        int[][] start = {{1,1},{1,0}};
        return matrixPow(start,n-1)[0][0];
    }
    public static int[][] matrixPow(int[][] start,int n){
         if((n&1)==0){
             int[][] temp = matrixPow(start,n>>1);
             return matrixMultiply(temp,temp);
         }
         else if(n==1){
             return start;
         }
         else{
             return matrixMultiply(start,matrixPow(start,n-1));
         }
    }
    public static int[][] matrixMultiply(int[][] x,int[][] y){
        int[][] result = new int[x.length][y[0].length];
        for(int i=0;i

排序算法比较表格
https://www.jianshu.com/p/6ae77d17170c

快排:
package chapter2;
public class P79_Sort {
    //数组快排,时间o(nlogn)(最差n^2),空间o(logn)(最差n),递归造成的栈空间的使用,不稳定
    public static void quickSort(int[] data){
        if(data==null || data.length<=1) return;
        quickSortCore(data,0,data.length-1);
    }
    public static void quickSortCore(int[] data,int start,int end){
        if(end-start<=0)
            return;
        int index = quickSortPartition(data,start,end);
        quickSortCore(data,start,index-1);
        quickSortCore(data,index+1,end);
    }
    public static int quickSortPartition(int[] data,int start,int end){
        //选择第一个值作为基准
        int pivot = data[start];
        int left = start,right = end;
        while(left=pivot)
                right--;
            if(left=end)
            return;
        int mid = start + (end - start)/2;
        mergeSortCore(data,start,mid);
        mergeSortCore(data,mid+1,end);
        mergeSortMerge(data,start,mid,end);
    }
    public static void mergeSortMerge(int[] data,int start,int mid,int end){
        if(end==start)
            return;
        int[] temp = new int[end-start+1];
        int left = start,right = mid+1,tempIndex = 0;
        while(left<=mid && right<=end){
            if(data[left] num[j]) {
                break;
            }
            //如果小于则将子节点移动到父节点位置
            num[i] = num[j];
            //继续向下调整
            i = j;
        }
        //最后插入数据
        num[i] = temp;
    }
    
    public static void HeapInit(int[] nums, int l) {
        for (int i = (l - 1) / 2; i >= 0; i--) {
            HeapAdjust(nums, i, l);
            System.out.println(Arrays.toString(nums)+", i:"+i);

        }
    }

    public static void HeapSort(int[] nums, int l) {
        for (int i = l; i > 0; i--) {
            //把大根堆的跟放到最后的位置也就是i
            int temp = nums[0];
            nums[0] = nums[i];
            nums[i] = temp;
            //然后每次都从根也就是0开始调堆,不调最后排好的位置
            HeapAdjust(nums, 0, i - 1);
            System.out.println(Arrays.toString(nums)+", l:"+i);

        }
    }

    public static void main(String[] args) {
        int[] nums = {0, 1, 2, 3, 4, 5, 6, 7, 8};
        HeapInit(nums, 8);
        HeapSort(nums, 8);
        System.out.println(Arrays.toString(nums));
        
    }
}

冒泡排序:
package chapter2;

/**
 * Created by ryder on 2017/6/25.
 * 数组排序算法
 */
public class P79_Sort {
    //数组冒泡,时间o(n^2),空间o(1),稳定
    public static void bubbleSort(int[] data){
        if(data==null || data.length<=1)
            return;
        for(int i=0;idata[j]){
                    int temp = data[j-1];
                    data[j-1] = data[j];
                    data[j] = temp;
                }
            }
        }
    }
    public static void testBubbleSort(){
        int[] data = {5,4,3,1,2};
        bubbleSort(data);
        System.out.print("数组冒泡排序:\t");
        for(int item: data){
            System.out.print(item);
            System.out.print('\t');
        }
        System.out.println();
    }
}
选择排序:
package chapter2;

/**
 * Created by ryder on 2017/6/25.
 * 数组排序算法
 */
public class P79_Sort {
    //数组选择排序,时间o(n^2),空间o(1),不稳定
    public static void selectionSort(int[] data){
        if(data==null || data.length<=1)
            return;
        for(int i=0;i0 && data[j-1]>temp) {
                data[j] = data[j-1];
                j--;
            }
            data[j] = temp;
        }
    }
    public static void testInsertionSort(){
        int[] data = {5,4,3,1,2};
        insertionSort(data);
        System.out.print("数组插入排序:\t");
        for(int item: data){
            System.out.print(item);
            System.out.print('\t');
        }
        System.out.println();
    }
}
希尔排序:
package chapter2;

/**
 * Created by ryder on 2017/6/25.
 * 数组排序算法
 */
public class P79_Sort {
    //数组希尔排序(插入排序缩小增量),时间o(n^1.3),空间o(1),不稳定
    //时间复杂度是模糊的,有人在大量的实验后得出结论:
    //当n在某个特定的范围后希尔排序的比较和移动次数减少至n^1.3。次数取值在1到2之间。
    public static void shellSort(int[] data){
        if(data==null || data.length<=1)
            return;
        for(int d=data.length/2; d>0; d=d/2){
            for(int i=d;i=d && data[cur-d]>temp){
                    data[cur] = data[cur-d];
                    cur = cur - d;
                }
                data[cur] = temp;
            }
        }
    }
    public static void testShellSort(){
        int[] data = {5,4,3,1,2};
        shellSort(data);
        System.out.print("数组希尔排序:\t");
        for(int item: data){
            System.out.print(item);
            System.out.print('\t');
        }
        System.out.println();
    }
}

  1. 旋转数组的最小数字
/**
 * Created by ryder on 2017/6/28.
 * 旋转数组的最小数字
 */

//把一个数组的若干元素搬到数组末尾叫旋转数组
public class P82_MinNumberInRotatedArray {
    public static int min(int[] data){
        if(data==null || data.length==0)
            return -1;
        int left = 0;
        int right = data.length-1;
        int mid;
        while(left right
            else if(data[left]>data[right]){
                //中间的大等左边,说明最小值在mid右边
                if(data[mid]>=data[left])
                    left = mid + 1;
                //否则在mid左边,包含mid
                else
                    right = mid;
            }
            //left = right
            else{
                //中间大于左边,说明最小值在mid右边
                if(data[left]data[mid])
                    right = mid;
                //不确定,只能缩小两个位置的范围
                else{
                    left = left+1;
                    right = right-1;
                }
            }
        }
        return data[right];
    }
    public static void main(String[] args){
        int[] data1 = {3,4,5,1,2};
        int[] data2 = {1,0,1,1,1};
        int[] data3 = {1,1,1,0,1};
        System.out.println(min(data1));
        System.out.println(min(data2));
        System.out.println(min(data3));
    }
}

  1. 矩阵中的路径
题目要求:设计一个函数,用来判断一个矩阵中是否存在一条包含某字符串的路径。  
(1)起点随意;(2)路径的移动只能是上下左右;(3)访问过的位置不能再访问。  
以下图矩阵为例,包含“bfce”,但是不包含“abfb”。  

a      b      t      g
c      f      c      s
j      d      e      h
/**
 * Created by ryder on 2017/7/2.
 * 矩阵中的路径
 */
public class P89_StringPathInMatrix {
    //回溯法解决
    public static boolean hasPath(char[][] data,String str){
        if(data==null || data.length==0 || str==null || str.length()==0)
            return false;
        int rowLen = data.length;
        int colLen = data[0].length;
        boolean[][] visitFlag = new boolean[rowLen][colLen];
        for(int row=0;row=str.length()) return true;
        if(rowIndex<0 || colIndex<0 || rowIndex>=data.length || colIndex>=data[0].length) 
            return false;

        //递归
        if(!visitFlag[rowIndex][colIndex]&&data[rowIndex][colIndex]==str.charAt(strIndex)){
            //如果未被访问,且匹配字符串,标记当前位置为已访问,分上下左右四个位置递归求解
            visitFlag[rowIndex][colIndex] = true;
            boolean result =  
                    hasPathCore(data,rowIndex+1,colIndex,visitFlag,str,strIndex+1) ||
                    hasPathCore(data,rowIndex-1,colIndex,visitFlag,str,strIndex+1) ||
                    hasPathCore(data,rowIndex,colIndex+1,visitFlag,str,strIndex+1) ||
                    hasPathCore(data,rowIndex,colIndex-1,visitFlag,str,strIndex+1);
            //已经求的结果,无需修改标记了
            if(result)
                return true;
            //当前递归的路线求解失败,要把这条线路上的标记清除掉
            //因为其他起点的路径依旧可以访问本路径上的节点。
            else{
                visitFlag[rowIndex][colIndex] = false;
                return false;
            }
        }
            else
            return false;
    }
    public static void main(String[] args){
        char[][] data = {
                {'a','b','t','g'},
                {'c','f','c','s'},
                {'j','d','e','h'}};
        System.out.println(hasPath(data,"bfce")); //true
        System.out.println(hasPath(data,"abfb")); //false,访问过的位置不能再访问
    }
}

  1. 机器人的运动范围
题目要求:
地上有一个m行n列的方格,一个机器人从坐标(0,0)的各自开始移动,它每次  
可以向上下左右移动一格,但不能进入横纵坐标数位之和大于k的格子。  
例如,当k等于18时,机器人能进入(35,37),因为3+5+3+7=18;但却不能进入  
(35,38),因为3+5+3+8=19>18。  
请问该机器人能够到达多少个格子。  

解题思路:  
本题依旧考察回溯法。  
每前进一步后,可选移动项为上下左右四个;为了判断某一个格子是否可以进入  
从而进行计数,不仅需要考虑边界值,计算各位数字之和,更要判断该格子是否  
已经被访问过,。所以需要一个布尔矩阵,用来记录各格子是否已被访问。整体思  
路与12题类似,具体请参考本系列的导航帖。  
package chapter2;
/**
 * Created by ryder on 2017/7/4.
 * 机器人的运动范围
 */
public class P92_RobotMove {
    //依旧回溯
    public static int movingCount(int threshold,int rowLen,int colLen){
        if(rowLen<=0 || colLen<=0 || threshold<0)
            return 0;
        boolean[][] visitFlag = new boolean[rowLen][colLen];
        for(int row=0;row=0 && col>=0 && row0){
            sum += number%10;
            number/=10;
        }
        return sum;
    }

    public static void main(String[] args){
        System.out.println(movingCount(0,3,4)); //1
        System.out.println(movingCount(1,3,4)); //3
        System.out.println(movingCount(9,2,20)); //36
    }
}
  1. 剪绳子
题目要求:  
给你一根长度为n的绳子,请把绳子剪成m段,记每段绳子长度为  k[0],k[1]...k[m-1],  
求k[0]k[1]...k[m-1]的最大值。已知绳子长度n为整数,m>1(至少要剪一刀,不能不剪),  
k[0],k[1]...k[m-1]均要求为整数。  
例如,绳子长度为8时,把它剪成3-3-2,得到最大乘积18;绳子长度为3时,把它剪成2-1,  
得到最大乘积2。  

我们定义长度为n的绳子剪切后的最大乘积为f(n),剪了一刀后,f(n)=max(f(i)*f(n-i));  
假设n为10,第一刀之后分为了4-6,而6也可能再分成2-4(6的最大是3-3,但过程中还是  
要比较2-4这种情况的),而上一步4-6中也需要求长度为4的问题的最大值,可见,各个子  
问题之间是有重叠的,所以可以先计算小问题,存储下每个小问题的结果,逐步往上,求得  
大问题的最优解。  

上述算法的时间复杂度为o(n^2);但其实,可以使用贪婪算法在o(1)时间内得到答案:  
n<5时,和动态规划一样特殊处理;n>=5时,尽可能多地剪长度为3的绳子,当剩下的绳子长  
度为4时,剪成2-2;比如长度为13的绳子, 剪成3-3-3-2-2;贪婪算法虽然快,但一般都思  
路奇特,可遇不可求。且面试官一般都会要求证明,数学功底要好 。  
/**
 * Created by ryder on 2017/7/5.
 * 剪绳子
 */
public class P96_CuttingRope {
    public static int maxCutting(int length){
        if(length<2) return 0;
        if(length==2)return 1;
        if(length==3)return 2;
        int[] dp = new int[length+1];
        dp[0]=0;
        dp[1]=1;
        dp[2]=2;
        dp[3]=3;
        int max = 0;
        int temp = 0;
        //i是绳子的长度,j是最后一次减去的长度,到总长度的一半
        for(int i=4;i<=length;i++){
            max = 0;
            for(int j=1;j<=i/2;j++){
                temp = dp[j]*dp[i-j];
                if(temp>max)
                    max = temp;
            }
            //dp[i]是temp里最大的
            dp[i] = max;
        }
        return dp[length];
    }
    public static void main(String[] args){
        for(int i=2;i<10;i++){
            System.out.println("长度为"+i+"的最大值->"+maxCutting(i));
        }
    }
}
  1. 位运算
题目要求:
实现一个函数,输入一个int型整数,输出该数字在计算机中二进制表示形式的1的个数。  
例如9->1001,输出2;-3->11111111111111111111111111111101,输出31。  

解题思路:
考查位运算,此题要注意负数的处理。首先要明确计算机中,数字是以补码的形式存储的,  
原码反码补码不清楚的话请自己谷歌百度。其次,明确位运算符,与&,或|,非~,异或^,  
<<左移位,>>带符号右移位,>>>无符号右移位(java有此符号,c++没有)

解法一:将数字无符号右移,直到为0。  

解法二:使用一个标记,初始为1,让标记值与原输入数字异或,然后标记值左移。解法一  
是原数字右移,而解法二是标记左移,从java来看思路类似但换了个角度;但这个思路在  
C++就很关键,因为C++中没有>>>运算符,只能用解法二。  

解法三:没接触过的人应该会觉得比较新颖。对于二进制数有如下结论:【把一个整数减去1  
之后再和原来的整数做位与运算,得到的结果相当于把原整数的二进制表示形式的最右边的  
1变成0】。比如1001,执行一次上述结论,1001&1000=1000,将最右边的1改为了0;再执行  
一次,1000&0111=0000,第二个1也改成了0。因此能执行几次该结论,就有几个1。  
对于解法一二,都需要循环32次,判断每一个比特位是否为1,而解法三,循环次数等于比特  
位为1的个数。时间上是有改进的。  
/**
 * Created by ryder on 2017/7/6.
 * 二进制中的1的个数
 */
public class P100_NumberOf1InBinary {
    public static int numberOfOne1(int n){
        int count=0;
        while(n!=0){
            if((n&1)!=0)
                count++;
            n>>>=1;
        }
        return count;
    }
    public static int numberOfOne2(int n){
        int count=0;
        int flag=1;
        while(flag!=0){
            if((n&flag)!=0)
                count++;
            flag<<=1;
        }
        return count;
    }
    public static int numberOfOne3(int n){
        int count=0;
        while(n!=0){
            n = n&(n-1);
            count++;
        }
        return count;
    }
    public static void main(String[] args){
        System.out.println(numberOfOne1(3));
        System.out.println(numberOfOne1(-3));
        System.out.println(numberOfOne2(3));
        System.out.println(numberOfOne2(-3));
        System.out.println(numberOfOne3(3));
        System.out.println(numberOfOne3(-3));
    }
}

  1. 数值的整数次方
题目要求:  
实现函数double power(double base,int exponent),求base的exponent次方。  
不能使用库函数,不需要考虑大数问题。  

解题思路:本题考查考虑问题的完整性。如下几个点要注意:  
要考虑一些特殊情况,如指数为负、指数为负且底数为0、0的0次方要定义为多少。  
底数为0的定义。对于一个double类型的数,判断它与另一个数是否相等,不能用“==”,  
一般都需要一个精度,见下面的equal函数。  
对于报错的情况,比如0的负数次方,要如何处理。书中提了三种错误处理方法:用函数  
返回值来提示错误;用一个全局变量来提示错误;抛出异常;三种方法优缺点比较如下  

错误处理方法             优点                        缺点  
返回值           和相关函数的API一致          不能方便地使用计算结果  
全局变量        能够方便地使用计算结果       用户可能忘记检查全局变量  
异常        可自定义异常类型,逻辑清晰明了   抛出异常时对性能有负面影响  
/**
 * Created by ryder on 2017/7/6.
 * 数值的整数次方
 */
public class P110_Power {
    static boolean invalidInput = false;
    public static double power(double base,int exponent){
        //0的0次方在数学上没有意义,为了方便也返回1,也可特殊处理
        if(exponent==0)
            return 1;
        if(exponent<0){
            if(equal(base,0)){
               //通过全局变量报错
                invalidInput = true;
                return 0;
            }
            else
                return 1.0/powerWithPositiveExponent(base,-1*exponent);
        }
        else
            return powerWithPositiveExponent(base,exponent);
    }
    public static boolean equal(double x,double y){
       return -0.00001>1);
            return temp*temp;
        }
        else{
            //为奇数,例如五次方,就是两个二次方再乘以一个base
            double temp = powerWithPositiveExponent(base,exponent>>1);
            return base*temp*temp;
        }
    }
    public static void main(String[] args){
        System.out.println("2^3="+power(2,3)+"\t是否报错:"+invalidInput);
        System.out.println("2^-3="+power(2,-3)+"\t是否报错:"+invalidInput);
        System.out.println("0^3="+power(0,3)+"\t是否报错:"+invalidInput);
        System.out.println("0^-3="+power(0,-3)+"\t是否报错:"+invalidInput);
    }
}

  1. 打印从1到最大的n位数
题目要求:  
比如输入2,打印1,2......98,99;  

解题思路:  
此题需要考虑大数问题。本帖是使用字符串模拟数字的加法。  
/**
 * Created by ryder on 2017/7/6.
 *
 */
public class P114_Print1ToMaxOfNDigits {
    //在字符串上模拟加法
    public static void print1ToMaxOfNDigits(int num){
        if(num<=0)
            return;
        StringBuilder number = new StringBuilder(num);
        for(int i=0;i=0;i--){
            if(str.charAt(i)<'9' && str.charAt(i)>='0'){
                str.setCharAt(i,(char)(str.charAt(i)+1));
                return true;
            }
            else if(str.charAt(i)=='9'){
                str.setCharAt(i,'0');
            }
            else{
                return false;
            }
        }
        return false;
    }
    //打印时从不为0的开始打印
    public static void printNumber(StringBuilder number){
        boolean flag = false;
        for(int i=0;i
  1. 删除链表的节点
题目要求:  
在o(1)时间内删除单链表的节点。  

解题思路:  
直接删除单链表某一节点,无法在o(1)时间得到该节点的前一个节点,  
因此无法完成题目要求。可以将欲删节点的后一个节点的值拷贝到欲删  
节点之上,删除欲删节点的后一个节点,从而可以在o(1)时间内完成  
删除。(对于尾节点,删除仍然需要o(n),其他点为o(1),因此平均时  
间复杂度为o(1),满足要求)  
package structure;
/**
 * Created by ryder on 2017/6/13.
 */
public class ListNode {
    public T val;
    public ListNode next;
    public ListNode(T val){
        this.val = val;
        this.next = null;
    }
    @Override
    public String toString() {
        StringBuilder ret = new StringBuilder();
        ret.append("[");
        for(ListNode cur = this;;cur=cur.next){
            if(cur==null){
                ret.deleteCharAt(ret.lastIndexOf(" "));
                ret.deleteCharAt(ret.lastIndexOf(","));
                break;
            }
            ret.append(cur.val);
            ret.append(", ");
        }
        ret.append("]");
        return ret.toString();
    }
}
package chapter3;
import structure.ListNode;
/**
 * Created by ryder on 2017/7/7.
 * o(1)时间删除链表的节点
 */
public class P119_DeleteNodeInList {
    public static ListNode deleteNode(ListNode head,ListNode node){
        if(node==head){
            return head.next;
        }
        //考虑下一个为空和不为空的情况
        else if(node.next!=null){
            node.val = node.next.val;
            node.next = node.next.next;
            return head;
        }
        else{
            ListNode temp=head;
            while(temp.next!=node)
                temp = temp.next;
            temp.next = null;
            return head;
        }
    }
    public static void main(String[] args){
        ListNode head = new ListNode<>(1);
        ListNode node2 = new ListNode<>(2);
        ListNode node3 = new ListNode<>(3);
        head.next = node2;
        node2.next = node3;
        System.out.println(head);
        head = deleteNode(head,node3);
        System.out.println(head);
        head = deleteNode(head,head);
        System.out.println(head);
    }
}
  1. 题目二:删除排序链表中重复的节点
题目要求:  
比如[1,2,2,3,3,3],删除之后为[1];  

解题思路:  
由于是已经排序好的链表,需要确定重复区域的长度,删除后还需要将被删去的前与后连接,  
所以需要三个节点pre,cur,post,cur-post为重复区域,删除后将pre与post.next连接即可。  
此外,要注意被删结点区域处在链表头部的情况,因为需要修改head。  
package structure;
/**
 * Created by ryder on 2017/6/13.
 */
public class ListNode {
    public T val;
    public ListNode next;
    public ListNode(T val){
        this.val = val;
        this.next = null;
    }
    @Override
    public String toString() {
        StringBuilder ret = new StringBuilder();
        ret.append("[");
        for(ListNode cur = this;;cur=cur.next){
            if(cur==null){
                ret.deleteCharAt(ret.lastIndexOf(" "));
                ret.deleteCharAt(ret.lastIndexOf(","));
                break;
            }
            ret.append(cur.val);
            ret.append(", ");
        }
        ret.append("]");
        return ret.toString();
    }
}
package chapter3;

import structure.ListNode;

/**
 * Created by ryder on 2017/7/7.
 * 删除排序链表中的重复节点
 */
public class P122_deleteDuplicatedNode {
    public static ListNode deleteDuplication(ListNode head){
        if(head==null||head.next==null)
            return head;
        ListNode pre = null;
        ListNode cur = head;
        ListNode post = head.next;
        boolean needDelete = false;
        while (post!=null){
            if(cur.val.equals(post.val)){
                needDelete = true;
                post=post.next;
            }
            else if(needDelete && !cur.val.equals(post.val)){
                if(pre==null)
                    head = post;
                else
                    pre.next=post;
                cur = post;
                post = post.next;
                needDelete = false;
            }
            else{
                pre = cur;
                cur = post;
                post = post.next;
            }
        }
        if(needDelete && pre!=null)
            pre.next = null;
        else if(needDelete && pre==null)
            head = null;
        return head;
    }
    public static void main(String[] args){
        ListNode head = new ListNode<>(1);
        head.next= new ListNode<>(1);
        head.next.next = new ListNode<>(2);
        head.next.next.next = new ListNode<>(2);
        head.next.next.next.next = new ListNode<>(2);
        head.next.next.next.next.next = new ListNode<>(3);
        System.out.println(head);
        head = deleteDuplication(head);
        System.out.println(head);
    }
}
  1. 正则表达式匹配
题目要求:  
实现正则表达式中.和*的功能。.表示任意一个字符,*表示他前面的字符的任意次(含0次)。  
比如aaa与a.a和ab*ac*a匹配,但与aa.a和ab*a不匹配。  

解题思路:  
.就相当于一个万能字符,正常匹配即可;但*的匹配会涉及到前一个字符。所以要分模式串后  
一个字符不是*或没有后一个字符,模式串后一个字符是*这几个大的情况,之再考虑.的问题。  
package chapter3;
/**
 * Created by ryder on 2017/7/13.
 * 正则表达式匹配
 * 完成.(任何一个字符)和*(前面字符的任意次数)
 */
public class P124_RegularExpressionsMatching {
    public static boolean match(String str,String pattern){
        if(str==null || pattern==null)
            return false;
        return matchCore(new StringBuilder(str),0,new StringBuilder(pattern),0);
    }
    public static boolean matchCore(StringBuilder str,Integer strIndex,StringBuilder pattern, Integer patternIndex){
        //如果匹配串和模式串都匹配结束
        if(strIndex==str.length() && patternIndex==pattern.length())
            return true;
        if(strIndex!=str.length() && patternIndex==pattern.length())
            return false;
        if(strIndex==str.length() && patternIndex!=pattern.length()) {
            if(patternIndex+1
  1. 表示数值的字符串
题目要求:  
判断一个字符串是否表示数值,如+100,5e2,-123,-1E-16都是,  
12e,1e3.14,+-5,1.2.3,12e+5.4都不是。  
提示:表示数值的字符串遵循模式A[.[B]][e|EC] 或者 .B[e|EC];  
A,B,C表示整数,|表示或。[]表示可有可无。  

解题思路:  
此题也没有没什么特殊思路,就按照A[.[B]][e|EC] 或者 .B[e|EC];  
A,B,C这两种模式匹配下即可。  
package chapter3;
/**
 * Created by ryder on 2017/7/13.
 * 表示数值的字符串
 */
public class P127_NumberStrings {
    public static boolean isNumeric(String str){
        //正确的形式:A[.[B]][e|EC] 或者 .B[e|EC];
        if(str==null||str.length()==0)
            return false;
        int index;
        if(str.charAt(0)!='.'){
            index = scanInteger(str,0);
            if(index==-1)
                return false;
            if(index==str.length())
                return true;
            if(str.charAt(index)=='.'){
                if(index==str.length()-1)
                    return true;
                index = scanInteger(str,index+1);
                if(index==str.length())
                    return true;
            }
            if(str.charAt(index)=='e'||str.charAt(index)=='E'){
                index = scanInteger(str,index+1);
                if(index==str.length())
                    return true;
                else
                    return false;
            }
            return false;
        }
        else{
            index = scanInteger(str,1);
            if(index==-1)
                return false;
            if(index==str.length())
                return true;
            if(str.charAt(index)=='e'||str.charAt(index)=='E'){
                index = scanInteger(str,index+1);
                if(index==str.length())
                    return true;
            }
            return false;

        }

    }
    public static int scanInteger(String str,Integer index){
        if(index>=str.length())
            return -1;
        if(str.charAt(index)=='+'||str.charAt(index)=='-')
            return scanUnsignedInteger(str,index+1);
        else
            return scanUnsignedInteger(str,index);
    }
    public static int scanUnsignedInteger(String str,Integer index){
        int origin = index;
        while(str.charAt(index)>='0'&&str.charAt(index)<='9'){
            index++;
            if(index==str.length())
                return index;
        }
        if(origin==index)
            index = -1;
        return index;
    }
    public static void main(String[] args){
        System.out.println(isNumeric("+100"));//true
        System.out.println(isNumeric("5e2")); //true
        System.out.println(isNumeric("-123"));//true
        System.out.println(isNumeric("3.1416"));//true
        System.out.println(isNumeric("-1E-16"));//true
        System.out.println(isNumeric(".6"));//true
        System.out.println(isNumeric("6."));//true
        System.out.println(isNumeric("12e"));//false
        System.out.println(isNumeric("1a3.14"));//false
        System.out.println(isNumeric("1.2.3"));//false
        System.out.println(isNumeric("+-5"));//false
        System.out.println(isNumeric("12e+5.4"));//false
    }
}

你可能感兴趣的:(sword20)