《Java練習題》Java進階練習題(四)


編程合集: https://www.cnblogs.com/jssj/p/12002760.html

前言:不僅僅要實現,更要提升性能,精益求精,用盡量少的時間復雜度和空間復雜度解決問題。

【程序78】
實現獲取下一個排列的函數,算法需要將給定數字序列重新排列成字典序中下一個更大的排列。
如果不存在下一個更大的排列,則將數字重新排列成最小的排列(即升序排列)。
必須原地修改,只允許使用額外常數空間。
以下是一些例子,輸入位於左側列,其相應輸出位於右側列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

/**
 * 實現獲取下一個排列的函數,算法需要將給定數字序列重新排列成字典序中下一個更大的排列。
 * 如果不存在下一個更大的排列,則將數字重新排列成最小的排列(即升序排列)。
 * 必須原地修改,只允許使用額外常數空間。
 * 以下是一些例子,輸入位於左側列,其相應輸出位於右側列。
 * 1,2,3 → 1,3,2
 * 3,2,1 → 1,2,3
 * 1,1,5 → 1,5,1
 */
public class Subject78 {

    public static void main(String[] args) {
        int[] arr = new int[]{2,3,1,3,3};
        new Subject78().nextPermutation(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]+" ");
        }
    }

    /**
     * 下一個最大值
     * @param nums
     */
    public void nextPermutation(int[] nums) {
        int lengths = nums.length;
        int size = -1;
        for (int i = lengths-1; i >= 0; i--) {
            if(i-1 >= 0 && nums[i-1] < nums[i]){
                size = i-1;
                break;
            }
        }
        //如果沒有最大的值了
        if(size == -1){
            for (int i = 0 ,j= lengths-1; i <= j ; i++,j--) {
                int tmp = 0;
                tmp = nums[i];
                nums[i] = nums[j];
                nums[j] = tmp;
            }
        }else{ //處理size后邊的數據,重新整理成一個最小數組。
            //找到比size位置大的數中的最小數。
            int tmp = nums[size+1];
            int sizeExchange = size+1;
            for (int i = size+1; i < lengths ; i++) {
                //這里可以優化
                if(nums[i] <= tmp && nums[i] > nums[size]){
                    tmp = nums[i];
                    sizeExchange = i;
                }
            }
            nums[sizeExchange] = nums[size];
            nums[size] = tmp;
            //剩余數據重新重小到大排序
            for (int i = size+1 ,j= lengths-1; i <= j ; i++,j--) {
                int tmp0 = 0;
                tmp0 = nums[i];
                nums[i] = nums[j];
                nums[j] = tmp0;
            }
        }
    }
}

時間復雜度:O(n)

運行結果:

【程序79】
給定一個只包含 '(' 和 ')' 的字符串,找出最長的包含有效括號的子串的長度。

import java.util.ArrayList;
import java.util.List;

/**
 * 給定一個只包含 '(' 和 ')' 的字符串,找出最長的包含有效括號的子串的長度。
 */
public class Subject79 {

    public static void main(String[] args) {

        System.out.println(new Subject79().longestValidParentheses("(()()()(()))))))"));
    }

    public int longestValidParentheses(String s) {
        int lengths = s.length();
        if(lengths <= 0){
            return 0;
        }
        char[] arr = s.toCharArray();

        List<Integer> list = new ArrayList<>();

        /**
         * 將不可以匹配的括號留下,並且記錄位置。
         */
        for (int i = 0; i < arr.length; i++) {
            if('(' == arr[i]){
                list.add(i);
            }else{
                int size = list.size();
                if(')' == arr[i] && list.size() > 0 && '(' == arr[list.get(size-1)]){
                    list.remove(size-1);
                }else{
                    list.add(i);
                }
            }
        }

        //獲取最大間隔時間
        int maxLength = 0;
        for (int i = 0; i < list.size() ; i++) {
            if( i == 0 ){
                maxLength = list.get(i);
            }else {
                int tmp = list.get(i) - list.get(i-1) -1;
                if(tmp > maxLength){
                    maxLength = tmp;
                }
            }
        }

        if(list.size() > 0){
            int endLength = lengths - list.get(list.size()-1) -1;
            if(endLength > maxLength){
                maxLength = endLength;
            }
        } else {
            maxLength = lengths;
        }

        return maxLength;
    }
}

時間復雜度:O(n)

運行結果:

【程序80】
假設按照升序排序的數組在預先未知的某個點上進行了旋轉。
( 例如,數組[0,1,2,4,5,6,7]可能變為[4,5,6,7,0,1,2])。
搜索一個給定的目標值,如果數組中存在這個目標值,則返回它的索引,否則返回-1。
你可以假設數組中不存在重復的元素。
你的算法時間復雜度必須是O(logn) 級別。

示例 1:
輸入: nums = [4,5,6,7,0,1,2], target = 0
輸出: 4

示例2:
輸入: nums = [4,5,6,7,0,1,2], target = 3
輸出: -1

/**
 * 假設按照升序排序的數組在預先未知的某個點上進行了旋轉。
 * ( 例如,數組[0,1,2,4,5,6,7]可能變為[4,5,6,7,0,1,2])。
 * 搜索一個給定的目標值,如果數組中存在這個目標值,則返回它的索引,否則返回-1。
 * 你可以假設數組中不存在重復的元素。
 * 你的算法時間復雜度必須是O(logn) 級別。
 *
 * 示例 1:
 * 輸入: nums = [4,5,6,7,0,1,2], target = 0
 * 輸出: 4
 *
 * 示例2:
 * 輸入: nums = [4,5,6,7,0,1,2], target = 3
 * 輸出: -1
 */
public class Subject80 {

    int [] nums;
    int target;

    public static void main(String[] args) {
        int[] nums = new int[]{1};
        System.out.println(new Subject80().search(nums,0));
    }

    public int search(int[] nums, int target) {
        this.nums = nums;
        this.target = target;

        int n = nums.length;

        if (n == 0)
            return -1;
        if (n == 1)
            return this.nums[0] == target ? 0 : -1;

        /**
         * 找到旋轉節點
         */
        int rotate_index = find_rotate_index(0, n - 1);

        // if target is the smallest element
        if (nums[rotate_index] == target)
            return rotate_index;
        // if array is not rotated, search in the entire array
        if (rotate_index == 0)
            return search(0, n - 1);
        if (target < nums[0])
            // search in the right side
            return search(rotate_index, n - 1);
        // search in the left side
        return search(0, rotate_index);
    }

    /**
     * 找旋轉節點
     * @param left
     * @param right
     * @return
     */
    public int find_rotate_index(int left, int right) {
        if (nums[left] < nums[right])
            return 0;

        while (left <= right) {
            int pivot = (left + right) / 2;
            if (nums[pivot] > nums[pivot + 1])
                return pivot + 1;
            else {
                if (nums[pivot] < nums[left])
                    right = pivot - 1;
                else
                    left = pivot + 1;
            }
        }
        return 0;
    }

    /**
     * Binary search 二分查找法
     * @param left
     * @param right
     * @return
     */
    public int search(int left, int right) {
        while (left <= right) {
            int pivot = (left + right) / 2;
            if (nums[pivot] == target)
                return pivot;
            else {
                if (target < nums[pivot])
                    right = pivot - 1;
                else
                    left = pivot + 1;
            }
        }
        return -1;
    }
}

時間復雜度:O(logN)

運行結果:

【程序81】
給定一個按照升序排列的整數數組 nums,和一個目標值 target。找出給定目標值在數組中的開始位置和結束位置。
你的算法時間復雜度必須是O(log n) 級別。
如果數組中不存在目標值,返回[-1, -1]。

/**
 * 給定一個按照升序排列的整數數組 nums,和一個目標值 target。找出給定目標值在數組中的開始位置和結束位置。
 * 你的算法時間復雜度必須是O(log n) 級別。
 * 如果數組中不存在目標值,返回[-1, -1]。
 */
public class Subject81 {
    public static void main(String[] args) {
        int[] arr = new int[]{1};
        int[] result = new Subject81().searchRange(arr,1);
        for (int i = 0; i < result.length; i++) {
            System.out.print(result[i]+" ");
        }
    }

    /**
     * 二分查找法
     * @param nums
     * @param target
     * @return
     */
    public int[] searchRange(int[] nums, int target) {
        int left = 0;
        int right = nums.length-1;
        int pivot = -1;
        boolean flag = false;
        while (left <= right) {
            pivot = (left + right) / 2;
            if (nums[pivot] == target) {
                flag = true;
                break;
            } else {
                if (target < nums[pivot])
                    right = pivot - 1;
                else
                    left = pivot + 1;
            }
        }
        if(!flag){
            pivot = -1;
        }
        if(pivot != -1){
            int leftTmp = pivot;
            int rightTmp = pivot;
            while(leftTmp >= 0){
                leftTmp = leftTmp-1;
                if(leftTmp < 0 || nums[leftTmp] != target){
                    break;
                }
            }
            while(rightTmp <=  nums.length-1){
                rightTmp = rightTmp+1;
                if(rightTmp > nums.length-1  ||nums[rightTmp] != target){
                    break;
                }
            }
            return new int[]{leftTmp+1,rightTmp-1};
        }else{
            return new int[]{-1,-1};
        }
    }
}

時間復雜度:O(log2n)

運行結果:

【程序82】
給定一個排序數組和一個目標值,在數組中找到目標值,並返回其索引。如果目標值不存在於數組中,返回它將會被按順序插入的位置。

/**
 * 給定一個排序數組和一個目標值,在數組中找到目標值,並返回其索引。如果目標值不存在於數組中,返回它將會被按順序插入的位置。
 */
public class Subject82 {

    public static void main(String[] args) {
        int[] arr = new int[]{1,3,4,5,6,7,9,10};
        System.out.println(new Subject82().searchInsert(arr,8));
    }

    public int searchInsert(int[] nums, int target) {
        if(nums.length < 0){
            return 0;
        }
        int size = this.search(0,nums.length-1,nums,target);
        if(nums[size] == target){
            return size;
        }else{
            if(nums[size] > target){
                return size;
            }else{
                return size+1;
            }
        }
    }

    /**
     * Binary search 二分查找法
     * @param left
     * @param right
     * @return
     */
    public int search(int left, int right,int[] nums, int target) {
        while (left <= right) {
            int pivot = (left + right) / 2;
            if (nums[pivot] == target)
                return pivot;
            else {
                if (target < nums[pivot])
                    right = pivot - 1;
                else
                    left = pivot + 1;
            }
        }
        if(right <= -1){
            return left;
        }
        if(left >= nums.length){
            return right;
        }
        return left <= right? left :right;
    }
}

時間復雜度:O(logn)

運行結果:

【程序83】
判斷一個9x9 的數獨是否有效。只需要根據以下規則,驗證已經填入的數字是否有效即可。
數字1-9在每一行只能出現一次。
數字1-9在每一列只能出現一次。
數字1-9在每一個以粗實線分隔的3x3宮內只能出現一次。

/**
 * 判斷一個9x9 的數獨是否有效。只需要根據以下規則,驗證已經填入的數字是否有效即可。
 * 數字1-9在每一行只能出現一次。
 * 數字1-9在每一列只能出現一次。
 * 數字1-9在每一個以粗實線分隔的3x3宮內只能出現一次。
 */
public class Subject83 {
    public static void main(String[] args) {
        char[][] board = new char[][]{
                {'.','.','.','.','5','.','.','1','.'},
                {'.','4','.','3','.','.','.','.','.'},
                {'.','.','.','.','.','3','.','.','1'},
                {'8','.','.','.','.','.','.','2','.'},
                {'.','.','2','.','7','.','.','.','.'},
                {'.','1','5','.','.','.','.','.','.'},
                {'.','.','.','.','.','2','.','.','.'},
                {'.','2','.','9','.','.','.','.','.'},
                {'.','.','4','.','.','.','.','.','.'}};
        System.out.println( new Subject83().isValidSudoku(board));
    }

    public boolean isValidSudoku(char[][] board) {
        int[] rowCnt = new int[9];
        int[] colCnt = new int[9];
        int[] boxCnt = new int[9];
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if ('.' == board[i][j]) {
                    continue;
                }
                //處理成int型
                int num = board[i][j] - 48;
                // 處理行
                if ((rowCnt[i] >> num) % 2 == 1) {
                    return false;
                } else {
                    rowCnt[i] += 1 << num;
                }
                // 處理列
                if ((colCnt[j] >> num) % 2 == 1) {
                    return false;
                } else {
                    colCnt[j] += 1 << num;
                }
                // 處理框
                int boxNum = i / 3 * 3 + j / 3;
                if ((boxCnt[boxNum] >> num) % 2 == 1) {
                    return false;
                } else {
                    boxCnt[boxNum] = boxCnt[boxNum] + (1 << num);
                }
            }
        }
        return true;
    }
}

時間復雜度:O(1)

運行結果:

【程序84】
編寫一個程序,通過已填充的空格來解決數獨問題。
一個數獨的解法需遵循如下規則:
數字1-9在每一行只能出現一次。
數字1-9在每一列只能出現一次。
數字1-9在每一個以粗實線分隔的3x3宮內只能出現一次。
空白格用'.'表示。

/**
 * 編寫一個程序,通過已填充的空格來解決數獨問題。
 * 一個數獨的解法需遵循如下規則:
 * 數字1-9在每一行只能出現一次。
 * 數字1-9在每一列只能出現一次。
 * 數字1-9在每一個以粗實線分隔的3x3宮內只能出現一次。
 * 空白格用'.'表示。
 */
public class Subject84 {
    public static void main(String[] args) {
        char[][] board = new char[][]{
                {'.','.','9','7','4','8','.','.','.'},
                {'7','.','.','.','.','.','.','.','.'},
                {'.','2','.','1','.','9','.','.','.'},
                {'.','.','7','.','.','.','2','4','.'},
                {'.','6','4','.','1','.','5','9','.'},
                {'.','9','8','.','.','.','3','.','.'},
                {'.','.','.','8','.','3','.','2','.'},
                {'.','.','.','.','.','.','.','.','6'},
                {'.','.','.','2','7','5','9','.','.'}};
        new Subject84().solveSudoku(board);
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                System.out.print(board[i][j]+" ");
            }
            System.out.println();
        }
    }

    // box size
    int n = 3;
    // row size
    int N = n * n;

    int [][] rows = new int[N][N + 1];
    int [][] columns = new int[N][N + 1];
    int [][] boxes = new int[N][N + 1];

    char[][] board;

    boolean sudokuSolved = false;

    public boolean couldPlace(int d, int row, int col) {
    /*
    檢查是否可以在(行,列)單元格中放置數字d
    */
        int idx = (row / n ) * n + col / n;
        return rows[row][d] + columns[col][d] + boxes[idx][d] == 0;
    }

    public void placeNumber(int d, int row, int col) {
    /*
    在(行,列)單元格中放置數字d
    */
        int idx = (row / n ) * n + col / n;

        rows[row][d]++;
        columns[col][d]++;
        boxes[idx][d]++;
        board[row][col] = (char)(d + '0');
    }

    public void removeNumber(int d, int row, int col) {
    /*
    刪除一個無法找到解決方案的數字
    */
        int idx = (row / n ) * n + col / n;
        rows[row][d]--;
        columns[col][d]--;
        boxes[idx][d]--;
        board[row][col] = '.';
    }

    public void placeNextNumbers(int row, int col) {
    /*
    遞歸調用回溯函數
    繼續放置數字
    直到我們找到解決辦法
    */
        // 如果我們在最后一個牢房里
        // 這意味着我們有辦法
        if ((col == N - 1) && (row == N - 1)) {
            sudokuSolved = true;
        }
        // 如果還沒有
        else {
            // 如果我們排在最后
            // 到下一排
            if (col == N - 1) backtrack(row + 1, 0);
                // go to the next column
            else backtrack(row, col + 1);
        }
    }

    public void backtrack(int row, int col) {
    /*
    回溯
    */
        // 如果單元格是空的
        if (board[row][col] == '.') {
            // 對從1到9的所有數字進行迭代
            for (int d = 1; d < 10; d++) {
                if (couldPlace(d, row, col)) {
                    placeNumber(d, row, col);
                    placeNextNumbers(row, col);
                    // 如果數獨問題解決了,就不必回頭了。
                    // 因為獨聯解決方案是有希望的
                    if (!sudokuSolved) removeNumber(d, row, col);
                }
            }
        }
        else placeNextNumbers(row, col);
    }

    public void solveSudoku(char[][] board) {
        this.board = board;

        // 初始化行、列和框
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                char num = board[i][j];
                if (num != '.') {
                    int d = Character.getNumericValue(num);
                    placeNumber(d, i, j);
                }
            }
        }
        backtrack(0, 0);
    }
}

時間復雜度:O(9!^9)

運行結果:

【程序85】
報數序列是一個整數序列,按照其中的整數的順序進行報數,得到下一個數。其前五項如下:
1. 1
2. 11
3. 21
4. 1211
5. 111221
1被讀作"one 1"("一個一") , 即11。
11 被讀作"two 1s"("兩個一"), 即21。
21 被讀作"one 2", "one 1"("一個二","一個一"), 即1211。
給定一個正整數 n(1 ≤n≤ 30),輸出報數序列的第 n 項。

/**
 * 報數序列是一個整數序列,按照其中的整數的順序進行報數,得到下一個數。其前五項如下:
 * 1.     1
 * 2.     11
 * 3.     21
 * 4.     1211
 * 5.     111221
 * 1被讀作"one 1"("一個一") , 即11。
 * 11 被讀作"two 1s"("兩個一"), 即21。
 * 21 被讀作"one 2", "one 1"("一個二","一個一"), 即1211。
 * 給定一個正整數 n(1 ≤n≤ 30),輸出報數序列的第 n 項。
 */
public class Subject85 {

    public static void main(String[] args) {
        System.out.println(new Subject85().countAndSay(6));
    }

    public String countAndSay(int n) {
        if(n == 1){
            return "1";
        }else{
            String str = countAndSay(n-1);
            char[] chArr = str.toCharArray();
            StringBuilder strTmp = new StringBuilder("");
            char ch = chArr[0] ;
            int count = 0;
            for (int i = 0; i < chArr.length; i++) {
                if(ch == chArr[i]){
                    count++;
                }else{
                    strTmp.append(count).append(ch);
                    ch = chArr[i];
                    count = 1;
                }
            }
            strTmp.append(count).append(ch);
            return strTmp.toString();
        }
    }
}

時間復雜度:O(n)

運行結果:

【程序86】
給定一個無重復元素的數組candidates和一個目標數target,找出candidates中所有可以使數字和為target的組合。
candidates中的數字可以無限制重復被選取。
說明:
所有數字(包括target)都是正整數。
解集不能包含重復的組合。

import java.util.*;

/**
 * 給定一個無重復元素的數組candidates和一個目標數target,找出candidates中所有可以使數字和為target的組合。
 * candidates中的數字可以無限制重復被選取。
 * 說明:
 * 所有數字(包括target)都是正整數。
 * 解集不能包含重復的組合。
 */
public class Subject86 {

    private List<List<Integer>> res = new ArrayList<>();
    private int[] candidates;
    private int len;

    public static void main(String[] args) {
        int[] candidates = new int[]{2,3,6,7};
        List<List<Integer>> list = new Subject86().combinationSum(candidates,7);
        System.out.println(list);
    }

    private void findCombinationSum(int residue, int start, Stack<Integer> pre) {
        if (residue == 0) {
            // Java 中可變對象是引用傳遞,因此需要將當前 path 里的值拷貝出來
            res.add(new ArrayList<>(pre));
            return;
        }
        // 優化添加的代碼2:在循環的時候做判斷,盡量避免系統棧的深度
        // residue - candidates[i] 表示下一輪的剩余,如果下一輪的剩余都小於 0 ,就沒有必要進行后面的循環了
        // 這一點基於原始數組是排序數組的前提,因為如果計算后面的剩余,只會越來越小
        for (int i = start; i < len && residue - candidates[i] >= 0; i++) {
            pre.add(candidates[i]);
            // 【關鍵】因為元素可以重復使用,這里遞歸傳遞下去的是 i 而不是 i + 1
            findCombinationSum(residue - candidates[i], i, pre);
            pre.pop();
        }
    }

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        int len = candidates.length;
        if (len == 0) {
            return res;
        }
        // 優化添加的代碼1:先對數組排序,可以提前終止判斷
        Arrays.sort(candidates);
        this.len = len;
        this.candidates = candidates;
        findCombinationSum(target, 0, new Stack<>());
        return res;
    }
}

時間復雜度:O(2^n)

運行結果:

【程序87】
給定一個數組candidates和一個目標數target,找出candidates中所有可以使數字和為target的組合。
candidates中的每個數字在每個組合中只能使用一次。
說明:
所有數字(包括目標數)都是正整數。
解集不能包含重復的組合。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;

/**
 * 給定一個數組candidates和一個目標數target,找出candidates中所有可以使數字和為target的組合。
 * candidates中的每個數字在每個組合中只能使用一次。
 * 說明:
 * 所有數字(包括目標數)都是正整數。
 * 解集不能包含重復的組合。
 */
public class Subject87 {

    private List<List<Integer>> res = new ArrayList<>();

    public static void main(String[] args) {
        int[] candidates = new int[]{10,1,2,7,6,1,5};
        List<List<Integer>> list = new Subject87().combinationSum2(candidates,7);
        System.out.println(list);
    }

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        int len = candidates.length;
        if (len == 0) {
            return res;
        }
        // 優化添加的代碼1:先對數組排序,可以提前終止判斷
        Arrays.sort(candidates);
        findCombinationSum(target, 0, new Stack<>(),candidates);
        return res;
    }

    private void findCombinationSum(int residue, int start, Stack<Integer> pre, int[] candidates) {
        if (residue == 0) {
            // Java 中可變對象是引用傳遞,因此需要將當前 path 里的值拷貝出來
            List list= new ArrayList<>(pre);
            res.add(list);
            return;
        }
        // 優化添加的代碼2:在循環的時候做判斷,盡量避免系統棧的深度
        // residue - candidates[i] 表示下一輪的剩余,如果下一輪的剩余都小於 0 ,就沒有必要進行后面的循環了
        // 這一點基於原始數組是排序數組的前提,因為如果計算后面的剩余,只會越來越小
        for (int i = start; i < candidates.length && residue - candidates[i] >= 0; i++) {
            if( i-1 >= 0 && candidates[i] == candidates[i-1]){
                continue;
            }
            pre.add(candidates[i]);
            // 【關鍵】因為元素可以重復使用,這里遞歸傳遞下去的是 i 而不是 i + 1
            findCombinationSum(residue - candidates[i], i, pre,this.copyArr2(candidates,i));
            pre.pop();
        }
    }

    public int[] copyArr2(int[] candidatesTmp,int index){
        int[] candidates = new int[candidatesTmp.length-1];
        for (int i = 0,j = 0; i < candidatesTmp.length; i++) {
            if(index == i){
               continue;
            }else{
                candidates[j++] = candidatesTmp[i];
            }
        }
        return candidates;
    }
}

時間復雜度:O(n!)

運行結果:

以上題目均來自:https://leetcode-cn.com/ ,如果你熱愛編碼,熱愛算法,該網站一定適合你。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM