一些需要烂熟于心的代码

1、二叉树

1.1、非递归遍历

1.1.1、前序

/**
     * 先序非递归遍历
     * 访问一个节点时候,若该节点左右孩子节点都存在,按照右孩子左孩子顺序压栈,若只存在一个孩子节点,直接压栈该孩子节点
     */
    public void firstTravel(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        //压入根节点
        stack.push(root);
        //栈非空,弹栈遍历该节点,然后
        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            System.out.print(node.val + " ");
            if (node.right != null)
                stack.push(node.right);
            if (node.left != null)
                stack.push(node.left);
        }
        System.out.println();
    }

 

1.1.2、中序

 /**
     * 中序非递归遍历
     * 思想:
     * 左-根-右:所以只要左孩子还有左子树就要先遍历左孩子的左子树
     * 1、从根节点开始将左孩子节点压栈,如果左孩子节点还有左子树继续将左孩子节点的左孩子节点压栈,一直这么持续操作,直到遇到null节点;
     * 2、此时栈顶节点就是该树(不特指整棵树)中序遍历第一个要访问的节点,弹出该节点;
     * 3、对该节点的右孩子节点重复1,2操作。
     */
    public void inTravel(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        TreeNode node = root;
        while (node != null || !stack.isEmpty()) {
            //一直压入左孩子节点,直到压入null
            if (node != null) {
                stack.push(node);
                //左孩子节点可能为null
                node = node.left;
            }
            //栈顶元素为null就弹栈一次
            else {
                //此时栈顶元素就是以该栈顶元素作为根节点的树的第一个被访问节点
                node = stack.pop();
                System.out.print(node.val + " ");
                //右孩子节点可能为null
                node = node.right;
            }
        }
        System.out.println();
    }

 

1.1.3、后序

public void lastTravel(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        TreeNode node = root;
        TreeNode pre = null;//记录上一次访问的节点
        while (!stack.isEmpty() || node != null) {
            //一直压入左孩子节点,直到压入null
            if (node != null) {
                stack.push(node);
                //左孩子节点可能为null,表示
                node = node.left;
            }
            //栈顶元素为null就弹栈一次
            else {
                //此时栈顶元素就是以该栈顶元素作为根节点的树的第一个被访问节点
                node = stack.pop();
                //没有右孩子或者右孩子已经被访问(右孩子节点就是上一次被访问的节点)
                if (node.right == null || pre == node.right) {
                    System.out.print(node.val + " ");
                    pre = node;//更新状态
                    node = null;
                }
                //栈顶节点存在右孩子,且没有被访问。
                else {
                    stack.push(node);
                    //右孩子节点可能为null
                    node = node.right;
                }
            }
        }
        System.out.println();
    }

 

1.1.4、层次

 public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        //we use LinkedList as a queue
        LinkedList<TreeNode> q = new LinkedList<TreeNode>();
        if(root == null) return res;
        TreeNode p = root;
        q.addLast(p);
        while(!q.isEmpty()){
            int tmpSize = q.size();
            List<Integer> tmpList = new ArrayList<Integer>();
            //Attention:i<tmpSize
            for(int i=0;i<tmpSize;i++){
                p = q.getFirst();
                q.poll();
                tmpList.add(p.val);
                if(p.left!=null)  q.addLast(p.left);
                if(p.right!=null) q.addLast(p.right);
            }
            res.add(tmpList);
        }
        return res;
    }

 1.2、根据前序和后序构造二叉树 | 推出后序

1.2.1、构造树

public TreeNode createTree(String preOrder, String inOrder) {
    if (preOrder.isEmpty()) {
      return null;
    }

    char rootValue = preOrder.charAt(0);
    int rootIndex = inOrder.indexOf(rootValue);

    TreeNode root = new TreeNode(rootValue);
    root.setLeft(
        createTree(
            preOrder.substring(1, 1 + rootIndex),
            inOrder.substring(0, rootIndex)));
    root.setRight(
        createTree(
            preOrder.substring(1 + rootIndex),
            inOrder.substring(1 + rootIndex)));

    return root;
  }

 

1.2.2、求后序

public String postOrder(String preOrder, String inOrder) {
    if (preOrder.isEmpty()) {
      return "";
    }

    char rootValue = preOrder.charAt(0);
    int rootIndex = inOrder.indexOf(rootValue);

    return
        postOrder(
            preOrder.substring(1, 1 + rootIndex),
            inOrder.substring(0, rootIndex)) +
        postOrder(
            preOrder.substring(1 + rootIndex),
            inOrder.substring(1 + rootIndex)) +
        rootValue;
  }

 

1.3、寻找中序遍历时的下一个节点

TreeNode

public class TreeNode {
  private final char value;
  private TreeNode left;
  private TreeNode right;
  private TreeNode parent;

  public TreeNode(char value) {
    this.value = value;
    this.left = null;
    this.right = null;
    this.parent = null;
  }

  public char getValue() {
    return value;
  }

  public TreeNode getLeft() {
    return left;
  }

  public void setLeft(TreeNode left) {
    this.left = left;
    if (this.left != null) {
      this.left.setParent(this);
    }
  }

  public TreeNode getRight() {
    return right;
  }

  public void setRight(TreeNode right) {
    this.right = right;
    if (this.right != null) {
      this.right.setParent(this);
    }
  }

  public TreeNode getParent() {
    return parent;
  }

  private void setParent(TreeNode parent) {
    this.parent = parent;
  }
}
View Code

 

对于parent节点的处理。

  public void setLeft(TreeNode left) {
    this.left = left;
    if (this.left != null) {
      this.left.setParent(this);
    }
  }
public void setRight(TreeNode right) {
    this.right = right;
    if (this.right != null) {
      this.right.setParent(this);
    }
  }private void setParent(TreeNode parent) {
    this.parent = parent;
  }

 

next()

public TreeNode next(TreeNode node) {
    if (node == null) {
      return null;
    }

    if (node.getRight() != null) {
      return first(node.getRight());
    } else {
      while(node.getParent() != null
          && node.getParent().getRight() == node) {
        node = node.getParent();
      }
      // now we have:
      // node.getParent() == null
      // || node is left child of its parent
      return node.getParent();
    }
  }
public TreeNode first(TreeNode root) {
    if (root == null) {
      return null;
    }

    TreeNode curNode = root;
    while(curNode.getLeft() != null) {
      curNode = curNode.getLeft();
    }
    return curNode;
  }

 

1.4、copy一棵树

1.4.1、递归

    public static TreeNode copyTree_preOrder_recursion(TreeNode node) {
        if (node == null)
            return null;
        TreeNode newNode = new TreeNode(node.getValue());
        newNode.setLeft(copyTree_preOrder_recursion(node.getLeft()));
        newNode.setRight(copyTree_preOrder_recursion(node.getRight()));

        return newNode;

    }

 

 1.4.2、非递归

// 两个栈:
    // 第一个栈遍历原来的树
    // 第二个栈创建新的节点并跟随第一个栈的遍历过程
    public static TreeNode copyTree_preOrder_loop(TreeNode node) {
        if (node == null)
            return null;

        Stack<TreeNode> stack = new Stack<>();
        stack.push(node);

        Stack<TreeNode> newStack = new Stack<>();
        TreeNode newRoot = new TreeNode(node.getValue());
        TreeNode newNode = newRoot;
        newStack.push(newNode);

        while (!stack.isEmpty()) {
            node = stack.pop();
            newNode = newStack.pop();
            if (node.getRight() != null) {
                stack.push(node.getRight());
                TreeNode rnode = new TreeNode(node.getRight().getValue());
                newNode.setRight(rnode);
                newStack.push(rnode);
            }
            if (node.getLeft() != null) {
                stack.push(node.getLeft());
                TreeNode lnode = new TreeNode(node.getLeft().getValue());
                newNode.setLeft(lnode);
                newStack.push(lnode);
            }
        }

        return newRoot;
    }

 

2、排序

https://www.cnblogs.com/chengxiao/p/6103002.html

https://blog.csdn.net/yushiyi6453/article/details/76407640

2.1、快排 

 

public static int partition(int[] numbers, int low,int high) {
        int temp = numbers[low]; //数组的第一个作为中轴
        while(low < high)  {     
            while(low < high && numbers[high] >= temp) high--;
            numbers[low] = numbers[high];//比中轴小的记录移到低端
            while(low < high && numbers[low] <= temp) low++;
            numbers[high] = numbers[low] ; //比中轴大的记录移到高端
        }
        numbers[low] = temp ; //中轴记录到尾
        return low ; // 返回中轴的位置
    }
public static void quickSort(int[] numbers,int low,int high) {
        if(low < high)   { // 不能用low==high作为递归结束条件
          int loc = partition(numbers,low,high); //将numbers数组进行一分为二
          quickSort(numbers, low, loc-1);   //对低字段表进行递归排序
          quickSort(numbers, loc+1, high); //对高字段表进行递归排序
        }    
    }

 partition的另外几种写法

选定第一个元素作为基点,便后后续所有为访问元素的过程中,对数据进行整理,使得数据一部分是小于key,另外一部分大于key;

在这个过程中需要记录分界点(哪部分小于key,哪部分大于key);

下面要讨论的就是如何将当前位置i元素该放入正确的位置。

 

二路快排:

 

三路快排: 

 

 

 //第一个元素作为基准值的另一种写法
    //[begin...i]比key小的元素
    //[i+1...j]比key大的元素
    //[j...end]未知的元素
    // j总是期望出现比key大的元素,一旦出现比key小的元素,就丢到i维护的区间内swap(i,j),
    // 即:i维护的区间增加1以容纳新的元素:i++,此时i位置的元素一定是大于key的,我们将其与此时j位置元素交换即可
    public int partitionBeign(int[] array, int begin, int end) {
        int key = array[begin];
        int i = begin;
        int j = begin + 1;
        for (; j <= end; j++) {
            if (array[j] < key) {
                i++;
                swap(array, i, j);
            }
        }
        swap(array, begin, i);
        return i;
    }

    //最后一位元素作为基准值
    // j总是期望出现比key大的元素,一但出现比key小的元素,就丢到i维护的区间内
    //在循环时arr[begin--i]表示的是不大于key的元素
    //arr[i+1--j]表示的是当前大于key的元素
    //arr[j---end]是未知的元素
    //划分的过程其实就是将每一个元素通过比较放到这两个区间去(主要是i的增长)。
    //当然最后还要将arr[i+1]和arr[r]交换,使得arr[i+1]表示划分元素
    public int partitionEnd(int[] array, int begin, int end) {
        int key = array[end];
        int i = begin - 1;
        int j = begin;
        for (; j <= end - 1; j++) {
            if (array[j] < key) {
                i++;
                swap(array, i, j);
            }
        }
        swap(array, i + 1, end);
        return i + 1;
    }
 public void quickSort(int[] array, int low, int high) {if (low < high) {
            int loc = partition(array, low, high);
            //int loc = partitionBeign(array, low, high);
            //int loc = partitionEnd(array, low, high);
            quickSort(array, low, loc - 1);
            quickSort(array, loc + 1, high);
        }
    }

2.2、希尔

int[] arrary = {49, 38, 65, 97, 76, 13, 27, 49, 55, 04};
int[] dk = {5, 3, 1};

public void shellInsert(int[] array, int dk) {
        int temp = 0, j = 0;
        // 遍历所有分组:若元素之间的距离为dk,则为同一个分组
        for (int i = dk; i < array.length; i++) {
            // i,i-dk为同一个分组:对同一个分组的元素进行直接插入排序
            if (array[i] < array[i - dk]) {
                temp = array[i];
                for (j = i - dk; j >= 0 && temp < array[j]; j -= dk) {
                    // 后移dk
                    array[j + dk] = array[j];
                }
                // 定位
                array[j + dk] = temp;
            }
        }
    }

    public void shellSort(int[] array, int[] dk, int t) {
        // 依次按照分组间隔dk进行操作
        for (int i = 0; i < t; i++) {
            shellInsert(array, dk[i]);
        }
    }

 

2.3、归并

 2.3.1、自顶向下

public void mergeSort(int[] data) {
        if (data == null || data.length == 0)
            return;
        int len = data.length;
        // 只开辟一次辅助数组 长度 n+1
        int[] temp = new int[len];
        mergeSortCore(data, 0, len - 1, temp);
    }
    // 将两个有序子数组合并[start-mid][mid+1,end],并将排序后数组写回原数组
    // temp:辅助数组
    public void merge(int[] data, int start, int mid, int end, int[] temp) {
        //前半段数组[start-mid];后半段数组[mid+1,end];一共为k个元素
        int i = start, j = mid + 1, k = 0;
        //子数组排序结果暂存于辅助数组中
        while (i <= mid && j <= end) {
            if (data[i] <= data[j])
                temp[k++] = data[i++];
            else
                temp[k++] = data[j++];
        }
        //前半段数组有剩余
        while (i <= mid)
            temp[k++] = data[i++];
        //后半段数组有剩余
        while (j <= end)
            temp[k++] = data[j++];
        //将排序后的子数组写回原数组,注意不是 i <= k
        for (i = 0; i < k; i++)
            data[start + i] = temp[i];
    }
    public void mergeSortCore(int[] data, int start, int end, int[] temp) {
        //递归终止;当数组长度变为1是,停止拆分
        if (start == end) // 或者 start < end 进行递归
            return;
        //拆分为左右两个子数组
        int mid = (start + end) / 2;
        mergeSortCore(data, start, mid, temp);//对左侧子数组递归排序;使得左侧子数组有序
        mergeSortCore(data, mid + 1, end, temp);//对右侧子数组递归排序;使得右侧子数组有序
        merge(data, start, mid, end, temp);//合并当前左右有序子数组
    }

2.3.2、自底向上

子数组大小分别取1,2,4,8.。。。;对其进行merger操作,最终得到有序数组。

public void mergeSortBU(int[] array, int n) {
        //辅助空间,用于merge过程
        int[] temp = new int[n];
        // 每次需要merge的数组规模
        for (int size = 1; size < n; size *= 2) {
            // 对该size规模下的全部数组进行merge
            for (int i = 0; i + size < n; i += 2 * size) {
                // 对[i...i+size-1] [i+size...i+2*size-1]进行merge
                merge(array, i, i + size - 1, Math.min(i + 2 * size - 1, n - 1), temp);
            }
        }
    }

    // 对[left...mid]与[mid+1...right]进行merge
    private void merge(int[] array, int left, int mid, int right, int[] temp) {
        int i = left, j = mid + 1, k = 0;
        while (i <= mid && j <= right) {
            if (array[i] <= array[j])
                temp[k++] = array[i++];
            else
                temp[k++] = array[j++];
        }
        while (i <= mid)
            temp[k++] = array[i++];
        while (j <= right)
            temp[k++] = array[j++];
        for (i = 0; i < k; i++) {
            array[left + i] = temp[i];
        }
    }

 

 我们知道,数组的一个最重要的特征就是通过索引来获取元素 item = arr[index],二此时的算法中,并没有使用这个重要性质,因此我们可以改进merger函数,将此方法 O(nlgn) 应用于链表数据的排序中。

2.4、桶排序与计数排序

https://www.jianshu.com/p/e6837a40bc30

2.5、堆排序

 

    //堆排序
    public void headSort(int[] array) {
        int n = array.length;
        //叶子节点本身就满足最大堆的定义:【完全二叉树】【大于孩子节点】
     //由完全二叉树的定义,只需要从最后一个非叶子节点开始调整。

     //调整为最大堆
for (int i = (n-1)/2; i >= 0; i--) { // 调整到位置 n-1
        headAdjust(array, i, n-1); } System.out.println(
"构建最大堆:"); System.out.println(Arrays.toString(array)); //排序 //每次拿出堆首元素,一次放在末尾位置l,对剩下的位置(l之前)进行调整,使其成为堆 for (int i = n-1; i > 0; i--) { // 交换堆首与末尾位置元素 swap(array, 0, i); // 从该末尾位置之前进行,调整,使其恢复为最大堆 headAdjust(array, 0, i - 1); } System.out.println("排序后"); System.out.println(Arrays.toString(array)); } // array编号从0开始, i:左孩子2i+1,右孩子2i+2 // 从堆顶开始层层调整 // 调整[r...l]使其成为最大堆 public void headAdjust(int array[], int r, int l) { int value = array[r]; while (2 * r + 1 <= l) { int i = 2 * r + 1; // 根节点的孩子节点中的较大者 if (i + 1 <= l && array[i] < array[i + 1]) i++; // 根节点大于最大孩子节点,无需调整 if (value >= array[i]) break; //需要调整,最大的孩子节点上位, swap(arrat,r,i);// 根节点还需要继续向下与 它的最大孩子节点的孩子节点 比较 r = i; } } public static void main(String[] args) { Sort sort = new Sort(); //堆排序 int[] data = {49, 38, 65, 97, 76, 13, 27, 49}; sort.headSort(data); }

 

 

结果

 

构建最大堆:
[97, 76, 65, 49, 49, 13, 27, 38]
排序后
[13, 27, 38, 49, 49, 65, 76, 97]

 

 

 函数定义一旦明确,就必须在后续使用过程中去维护这个定义,比如 heapJust(int [ ] array,int r,int l ),函数语义是调整 [ r ... l ] 范围中的元素使得给范围内元素符合最大堆的定义,即:无需考虑 [ l+1 ... array.length-1 ] 位置上的元素。如果我们将 heapJust(int [ ] array,int r,int l )语义改为调整在 [ r ... l ] 范围中调整 [ r ... l -1] 使得该范围的元素符合最大堆的定义,而不考虑 [ l ... array.lentgh-1 ] 的元素,当然在具体的代码上边界条件肯定需要发生变化。为了区分这里将 heapJust 改为 shiftDown   

 

   //空间复杂度O(1),直接在原数组中进行排序
    //标号从0开始
    //节点i: 父节点: (i-1)/2
    //      左孩子: 2*i+1   右孩子: 2*i+2
    public void heapSort(int[] arr, int n) {
        // 从最后一个非叶子节点开始调整->堆
        for (int i = (n - 1) / 2; i >= 0; i--) {
            // 每次都调整到 n-1 位置
            shfitDown(arr, i, n);
        }
        System.out.println("构建最大堆:");
        System.out.println(Arrays.toString(arr));

        //排序
        //交换首元素与堆范围内最后一个元素
        for (int i = n - 1; i > 0; i--) {
            //堆首与末尾位置互换
            swap(arr, 0, i);
            //需要从堆首(新元素开始)一直调整到上一个末尾位置之前(i-1)
            shfitDown(arr, 0, i);
        }
        System.out.println("排序:");
        System.out.println(Arrays.toString(arr));
    }

    // [i..n]中,对[i...i-1]进行调整,使其成为最大堆
    public void shfitDown(int[] arr, int i, int n) {
        // 存在孩子
        // 除了最后一个非叶子节点,完全二叉树性质,只要该节点存在左孩子,一定也存在右孩子
        while (2 * i + 1 < n) {
            int j = 2 * i + 1;
            if (j + 1 < n && arr[j] < arr[j + 1]) j++;
            if (arr[i] >= arr[j])
                break;
            swap(arr,i,j);
            i = j;
        }
    }

 

 

 

结果

构建最大堆:
[100, 98, 89, 75, 18, 78, 43, 26, 47, 3]
排序:
[3, 18, 26, 43, 47, 75, 78, 89, 98, 100]

 

3、栈

3.1、括号匹配

// 碰到左括号压栈;碰到右括号不压栈,但是判断栈顶元素是否匹配
    public boolean matchJudge(String str) {
        HashMap<Character, Character> dict = new HashMap<>();
        dict.put('(', ')');
        dict.put('[', ']');
        dict.put('{', '}');
        Stack<Character> stack = new Stack();
        boolean res = false;
        int len = str.length();
        //逐个字符
        char ch = '\0';for (int i = 0; i < len; i++) {
            ch = str.charAt(i);
            // 左括号,压栈
            if (dict.containsKey(ch))
                stack.push(ch);
                // 右括号
            else if (dict.containsValue(ch)) {
                // 栈顶元素与右括号匹配
                if (!stack.isEmpty() && dict.get(stack.pop()) == ch);
                    // 不匹配
                else return false;

            }
        }
        return stack.isEmpty() ? true : false;
    }

    //测试数据
    public String getMatchData(int index) {
        String str = "";
        String str1 = "{[(1+2)/(3+4)]-[[(1+2)/(3+4)]]}";
        String str2 = "{[()]}";
        String str3 = "{[]]}";
        String str4 = "[()(]";
        String str5 = "([)]";
        String str6 = "({}[]]])";
        String str7 = "[()](((";
        switch (index) {
            case 1: { str = str1; break; }
            case 2: { str = str2; break; }
            case 3: { str = str3; break; }
            case 4: { str = str4; break; }
            case 5: { str = str5; break; }
            case 6: { str = str6; break; }
            case 7: { str = str7; break; }
        }
        return str;
    }

 3.2、表达式求值

有两个表达式

  (4 + (13 / 5)) = 6        (a)

  ((2 + 1) * 3) = 9    (b)

 public int evalRPN(String[] tokens) {
        int res = 0;
        if (tokens == null || tokens.length == 0)
            return res;
        int len = tokens.length;
        Stack<Integer> stack = new Stack();
        int i = 0;
        for (; i < len; i++) {
            if (isNum(tokens[i]))
                stack.push(Integer.parseInt(tokens[i]));
            else {
                int a = stack.pop();
                int b = stack.pop();
                //除法有顺序要求哦
                stack.push(operator(b, a, tokens[i]));
            }
        }
        if (i == len)
            return stack.pop();
        return res;
    }

    private boolean isNum(String str) {
        boolean b = false;
        try {
            Integer.parseInt(str);
            b = true;
        } catch (Exception e) { }
        return b;
    }

    private int operator(int a, int b, String s) {
        int res = 0;
        switch (s) {
            case "+": { res = a + b; break; }
            case "-": { res = a - b; break; }
            case "*": { res = a * b; break; }
            case "/": { res = a / b; break; }
        }
        return res;
    }

3.3、用两个栈来模拟队列

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
     
    public void push(int node) {
        stack1.push(node);
    }
     
    public int pop() {
        if(stack1.empty()&&stack2.empty()){
            throw new RuntimeException("Queue is empty!");
        }
        if(stack2.empty()){
            while(!stack1.empty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}

 

4、链表

4.1、有序链表合并

4.4.1、非递归

public ListNode mergetTwoList(ListNode cur1, ListNode cur2) {
        // 将元首较小的链表作为cur1
        if (cur1.val > cur2.val) {
            ListNode t = cur1;
            cur1 = cur2;
            cur2 = t;
        }
        ListNode head = cur1;
        //记录cur1中当前访问节点的上一个访问节点
        ListNode pre = null;
        //临时变量
        ListNode q = null;
        while (cur1 != null && cur2 != null) {
            //当前:1中节点<=2中节点
            if (cur1.val <= cur2.val) {
                pre = cur1;//更新,1中上一个访问节点
                cur1 = cur1.next;//1中节点后移
            }
            //当前:1中节点>2中节点
            else {
                //临时记录当前2中节点的下一个节点
                q = cur2.next;
                //将2中当前节点,插入到1链表的preNode与curNode之间
                pre.next = cur2;
                cur2.next = cur1;
          // 1中当前节点的上一个访问节点变成了新插入的节点,需要更新pre状态
                pre = cur2;
          // 2中节点后移,继续在下一次与1中节点进行比较
                cur2 = q;
            }
        }
        if (cur2 != null)
            pre.next = cur2;
        return head;
    }
1 4 7 
2 5 6 
1 2 4 5 6 7 
---------------------
1 4 7 
0 5 6 
0 1 4 5 6 7 

 4.1.2、递归写法

//递归合并两个有序链表
    public ListNode mergeTwoListR(ListNode list1, ListNode list2) {
        ListNode head = null;
        if (list1 == null)
            return list2;
        if (list2 == null)
            return list1;
        if (list1.val <= list2.val) {
            head = list1;
            head.next = mergeTowListR(list1.next, list2);
        } else {
            head = list2;
            head.next = mergeTowListR(list2.next, list1);
        }
        return head;
    }

4.2、链表排序

O(nlgn)时间复杂度 :归并,快排

归并:

/**
     * 归并
     * 核心思想:利用快慢指针将链表拆分成两份,对其递归进行归并排序
     */
    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null)
            return head;
        ListNode mid = divideList(head);
        ListNode right = sortList(mid.next);
        mid.next = null;
        ListNode left = sortList(head);
        //return mergeTwoList(left, right);
        return mergeTwoListR(left, right);
    }
    //分裂链表,得到分裂点
    private ListNode divideList(ListNode head) {
        ListNode slow = head;
        ListNode fast = head.next;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

4.3、 链表反转

链表数据结构

public class Node<T> {
  private final T value;
  private Node<T> next;

  public Node(T value) {
    this.value = value;
    this.next = null;
  }

  public T getValue() {
    return value;
  }

  public Node<T> getNext() {
    return next;
  }

  public void setNext(Node<T> next) {
    this.next = next;
  }

  public static <T> void printLinkedList(Node<T> head) {
    while(head != null) {
      System.out.print(head.getValue());
      System.out.print(" ");
      head = head.getNext();
    }
    System.out.println();
  }
}
View Code

 

4.3.1、递归

  /**
   * Reverses a linked list.
   *
   * @param head the linked list to reverse
   * @return head of the reversed linked list
   */
  public <T> Node<T> reverseLinkedList(Node<T> head) {
    // size == 0 or size == 1
    if (head == null || head.getNext() == null) {
      return head;
    }

    Node<T> newHead = reverseLinkedList(head.getNext());
    head.getNext().setNext(head);
    head.setNext(null);
    return newHead;
  }

 

 

4.3.2、非递归

public <T> Node<T> reverseLinkedList(Node<T> head) {
    Node<T> newHead = null;
    Node<T> curHead = head;
    // Loop invariant:
    // newHead points to the linked list already reversed.
    // curHead points to the linked list not yet reversed.

    // Loop invariant holds.
    while(curHead != null) {
      // Loop invariant holds.
      Node<T> next = curHead.getNext();
      curHead.setNext(newHead);
      newHead = curHead;
      curHead = next;
      // Loop invariant holds.
    }
    // Loop invariant holds.
    return newHead;
  }

 

4.4、删除链表中值为value的节点

public <T> Node<T> deleteIfEquals(Node<T> head, T value) {
    while (head != null && head.getValue() == value) {
      head = head.getNext();
    }

    if (head == null) {
      return null;
    }

    Node<T> prev = head;
    // Loop invariant: list nodes from head up to prev has been
    // processed. (Nodes with values equal to value are deleted.)
    while(prev.getNext() != null) {
      if (prev.getNext().getValue() == value) {
        // delete it
        prev.setNext(prev.getNext().getNext());
      } else {
        prev = prev.getNext();
      }
    }

    return head;
  }

 4.5、判断链表是否有环,求环节入口节点

 

链接:https://www.nowcoder.com/questionTerminal/253d2c59ec3e4bc68da16833f79a38e4?toCommentId=1586973
来源:牛客网

fast,从起点开始每次移动2;
slow,从起点开始每次移动1;
若存在环,则fast与slow一定在环中相遇,相遇点为B;
入口点C到相遇点B的距离记为a(蓝色)s-a为剩余路径(橙色),整个环的长度为s(无需计算具体值);
起点A到入口点C的距离设为x;
相遇时:
Sfast = 2Sslow;
Sfast = x+n*s+a;
Sslow = x+m*s+a;
=>: x=(n-2m)s-a=(n-2m-1)s+s-a;
可以看出:A->C的路径x恰好是整数倍环的长度+橙色长度;
此时,一个指针从A移动1,另一个指针从B移动1,下次相遇点一定是入口C;
链接:https://www.nowcoder.com/questionTerminal/253d2c59ec3e4bc68da16833f79a38e4?toCommentId=1586973
来源:牛客网

// 用集合辅助
    public ListNode EntryNodeOfLoop(ListNode pHead) {
        ArrayList<ListNode> list = new ArrayList<>();
        ListNode node = pHead;
        while (node != null) {
            list.add(node);
            node = node.next;
            if (list.contains(node))
                return node;
        }
 
        return null;
    }   
 
   //剑指offer思路:快慢指针
    public ListNode EntryNodeOfLoop(ListNode pHead) {
        if(pHead == null)
            return null;
        if (pHead.next == pHead)
            return pHead;
        ListNode fast = pHead;//移动2
        ListNode slow = pHead;//移动1
        ListNode meet = null;//相遇点
        //寻找相遇点
         while (meet == null && fast.next != null && slow != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow)
                meet = fast;
        }
        //一个从头开始,一个从相遇点开始,均移动1,再次相遇即是入口点
        fast = pHead;
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }
        return meet != null ? fast : meet;
    }

 

 

5、查找

5.1、二分查找

 /**
   * Searches element k in a sorted array.
   * @param arr a sorted array
   * @param k the element to search
   * @return index in arr where k is. -1 if not found.
   */
  public int binarySearch(int[] arr, int k) {
    int a = 0;
    int b = arr.length;
    // Loop invariant: [a, b) is a valid range. (a <= b)
    // k may only be within range [a, b).
    while (a < b) {
      int m = a + (b - a) / 2; // m = (a + b) / 2 may overflow!
      if (k < arr[m]) {
        b = m;
      } else if (k > arr[m]) {
        a = m + 1;
      } else {
        return m;
      }
    }
    return -1;
  }

 

6、递归

6.1、combination

/**
     * Generates all combinations and output them,
     * selecting n elements from data.
     */
    public void combinations(
            List<Integer> selected, List<Integer> data, int n) {
        if (n == 0) {
            // output all selected elements
            for (Integer i : selected) {
                System.out.print(i);
                System.out.print(" ");
            }
            System.out.println();
            return;
        }

        if (data.isEmpty()) {
            return;
        }

        // select element 0
        selected.add(data.get(0));
        combinations(selected, data.subList(1, data.size()), n - 1);

        // un-select element 0
        //selected.remove(data.get(0));
        selected.remove(selected.size() - 1);
        combinations(selected, data.subList(1, data.size()), n);
    }

 

7、单例

public class Singleton {   
    private static Singleton = new Singleton();
    private Singleton() {}
    public static Singleton getSignleton(){
        return singleton;
    }
}
public class Singleton {
    private static volatile Singleton singleton = null;
    
    private Singleton(){}
    
    public static Singleton getSingleton(){
        if(singleton == null){
            synchronized (Singleton.class){
                if(singleton == null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }    
}

 

posted @ 2018-08-28 22:51  木子木泗  阅读(572)  评论(0编辑  收藏  举报