《剑指offer》专项突破

这篇具有很好参考价值的文章主要介绍了《剑指offer》专项突破。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

第一章:整数

面试题1:整数除法

题目

输入两个int型整数,求它们除法的商,要求不得使用乘号’*‘、除号’/‘以及求余符号’%'。当发生溢出时返回最大的整数值。假设除数不为0。例如,输入152,输出15/2的结果,即7

参考代码

public int divide(int dividend, int divisor) {
   
    if (dividend == 0x80000000 && divisor == -1){
   
        return Integer.MAX_VALUE;
    }

    int negative = 2;
    if (dividend > 0) {
   
        negative--;
        dividend = -dividend;
    }

    if (divisor > 0) {
   
        negative--;
        divisor = -divisor;
    }

    int result = divideCore(dividend, divisor);
    return negative == 1 ? -result : result;
}

private int divideCore(int dividend, int divisor) {
   
    int result = 0;
    while (dividend <= divisor) {
   
        int value = divisor;
        int quotient = 1;
        while (value >= 0xc0000000 && dividend <= value + value) {
   
            quotient += quotient;
            value += value;
        }

        result += quotient;
        dividend -= value;
    }

    return result;
}

面试题2:二进制加法

题目

输入两个表示二进制的字符串,请计算它们的和,并以二进制字符串的形式输出。例如输入的二进制字符串分别是"11"和"10",则输出"101"。

参考代码

public String addBinary(String a, String b) {
   
    StringBuilder result = new StringBuilder();
    int i = a.length() - 1;
    int j = b.length() - 1;
    int carry = 0;
    while (i >= 0 || j >= 0) {
   
        int digitA = i >= 0 ? a.charAt(i--) - '0' : 0;
        int digitB = j >= 0 ? b.charAt(j--) - '0' : 0;
        int sum = digitA + digitB + carry;
        carry = sum >= 2 ? 1 : 0;
        sum = sum >= 2 ? sum - 2 : sum;
        result.append(sum);
    }

    if (carry == 1) {
   
        result.append(1);
    }

    return result.reverse().toString();
}

面试题3:前n个数字二进制中1的个数

题目

输入一个非负数n,请计算0n之间每个数字的二进制表示中1的个数,并输出一个数组。例如,输入n4,由于0、1、2、3、4的二进制表示的1的个数分别为0、1、1、2、1,因此输出数组[0, 1, 1, 2, 1]

参考代码

解法一
public int[] countBits(int num) {
   
    int[] result = new int[num + 1];
    for (int i = 0; i <= num; ++i) {
   
        int count = 0;
        int j = i;
        while (j != 0) {
   
            result[i]++;
            j = j & (j - 1);
        }
    }

    return result;
}
解法二
public int[] countBits(int num) {
   
    int[] result = new int[num + 1];
    for (int i = 1; i <= num; ++i) {
   
        result[i] = result[i & (i - 1)] + 1;
    }

    return result;
}
解法三
public int[] countBits(int num) {
   
    int[] result = new int[num + 1];
    for (int i = 1; i <= num; ++i) {
   
        result[i] = result[i >> 1] + (i & 1);
    }

    return result;
}

面试题4:只出现一次的数字

题目

输入一个整数数组,数组中除一个数字只出现一次之外其他数字都出现三次。请找出那个唯一只出现一次的数字。例如,如果输入的数组为[0, 1, 0, 1, 0, 1, 100],则只出现一次的数字时100

参考代码

public int singleNumber(int[] nums) {
   
    int[] bitSums = new int[32];
    for (int num : nums) {
   
        for (int i = 0; i < 32; i++) {
   
            bitSums[i] += (num >> (31 - i)) & 1;
        }
    }

    int result = 0;
    for (int i = 0; i < 32; i++) {
   
        result = (result << 1) + bitSums[i] % 3;
    }

    return result;
}

面试题5:单词长度的最大乘积

题目

输入一个字符串数组words,请计算当两个字符串words[i]words[j]不包含相同字符时它们长度的乘积的最大值。如果没有不包含相同字符的一对字符串,那么返回0。假设字符串中只包含英语的小写字母。例如,输入的字符串数组words["abcw", "foo", "bar", "fxyz","abcdef"],数组中的字符串"bar"与"foo"没有相同的字符,它们长度的乘积为9。“abcw"与” fxyz "也没有相同的字符,它们长度的乘积是16,这是不含相同字符的一对字符串的长度乘积的最大值。

参考代码

public int maxProduct(String[] words) {
   
    boolean[][] flags = new boolean[words.length][26];
    for (int i = 0; i < words.length; i++) {
   
        for(char c: words[i].toCharArray()) {
   
            flags[i][c - 'a'] = true;
        }
    }

    int result = 0;
    for (int i = 0; i < words.length; i++) {
   
        for (int j = i + 1; j < words.length; j++) {
   
            int k = 0;
            for (; k < 26; k++) {
   
                if (flags[i][k] && flags[j][k]) {
   
                    break;
                }
            }

            if (k == 26) {
   
                int prod = words[i].length() * words[j].length();
                result = Math.max(result, prod);
            }
        }
    }

    return result;
}

第二章:数组

面试题6:排序数组中两个数字之和

题目

输入一个递增排序的数组和一个值k,请问如何在数组中找出两个和为k的数字并返回它们的下标?假设数组中存在且只存在一对符合条件的数字,同时一个数字不能使用两次。例如输入数组[1, 2, 4, 6, 10]k的值为8,数组中的数字26的和为8,它们的下标分别为13

参考代码

public int[] twoSum(int[] numbers, int target) {
   
    int i = 0;
    int j = numbers.length - 1;
    while (i < j && numbers[i] + numbers[j] != target) {
   
        if (numbers[i] + numbers[j] < target) {
   
            i++;
        } else {
   
            j--;
        }
    }

    return new int[] {
   i, j};
}

面试题7:数组中和为0的三个数字

题目

输入一个数组,如何找出数组中所有和为0的三个数字的三元组?注意返回值中不得包含重复的三元组。例如在数组中[-1, 0, 1, 2, -1, -4]中有两个三元组的和为0,它们分别是[-1, 0, 1][-1, -1, 2]

参考代码

public List<List<Integer>> threeSum(int[] nums) {
   
    List<List<Integer>> result = new LinkedList<List<Integer>>();
    if (nums.length >= 3) {
   
        Arrays.sort(nums);

        int i = 0;
        while(i < nums.length - 2) {
   
            twoSum(nums, i, result);

            int temp = nums[i];
            while(i < nums.length && nums[i] == temp) {
   
                ++i;
            }
        }
    }

    return result;
}

private void twoSum(int[] nums, int i, List<List<Integer>> result) {
   
    int j = i + 1;
    int k = nums.length - 1;
    while (j < k) {
   
        if (nums[i] + nums[j] + nums[k] == 0) {
   
            result.add(Arrays.asList(nums[i], nums[j], nums[k]));

            int temp = nums[j];
            while (nums[j] == temp && j < k) {
   
                ++j;
            }
        } else if (nums[i] + nums[j] + nums[k] < 0) {
   
            ++j;
        } else {
   
            --k;
        }
    }
}

面试题8:和大于等于k的最短子数组

题目

输入一个正整数组成的数组和一个正整数k,请问数组中和大于或等于k的连续子数组的最短长度是多少?如果不存在所有数字之和大于k的子数组,则返回0。例如输入数组[5, 1, 4, 3]k的值为7,和大于或等于7的最短连续子数组是[4, 3],因此输出它的长度2

参考代码

public int minSubArrayLen(int k, int[] nums) {
   
    int left = 0;
    int sum = 0;
    int minLength = Integer.MAX_VALUE;
    for (int right = 0; right < nums.length; right++) {
   
        sum += nums[right];
        while (left <= right && sum >= k) {
   
            minLength = Math.min(minLength, right - left + 1);
            sum -= nums[left++];
        }
    }

    return minLength == Integer.MAX_VALUE ? 0 : minLength;
}

面试题9:乘积小于k的子数组

题目

输入一个由正整数组成的数组和一个正整数k,请问数组中有多少个数字乘积小于k的连续子数组?例如输入数组[10, 5, 2, 6]k的值为100,有8个子数组的所有数字的乘积小于100,它们分别是[10]、[5]、[2]、[6]、[10, 5]、[5, 2]、[2, 6]和[5, 2, 6]

参考代码

public int numSubarrayProductLessThanK(int[] nums, int k) {
   
    long product = 1;
    int left = 0;
    int count = 0;
    for (int right = 0; right < nums.length; ++right) {
   
        product *= nums[right];
        while (left <= right && product >= k) {
   
            product /= nums[left];
            left++;
        }

        count += right >= left ? right - left + 1 : 0;
    }

    return count;
}

面试题10:和为k的子数组

题目:输入一个整数数组和一个整数k,请问数组中有多少个数字之和等于k的连续子数组?例如输入数组[1, 1, 1]k的值为2,有2个连续子数组之和等于2

参考代码

public int subarraySum(int[] nums, int k) {
   
    Map<Integer, Integer> sumToCount = new HashMap<>();
    sumToCount.put(0, 1);
    int sum = 0;
    int count = 0;
    for (int num : nums) {
   
        sum += num;
        count += sumToCount.getOrDefault(sum - k, 0);
        sumToCount.put(sum, sumToCount.getOrDefault(sum, 0) + 1);
    }

    return count;
}

面试题11:0和1个数相同的子数组

题目

输入一个只包含01的数组,请问如何求最长01的个数相同的连续子数组的长度?例如在数组[0, 1, 0]中有两个子数组包含相同个数的01,分别是[0, 1][1, 0],它们的长度都是2,因此输出2

参考代码

public int findMaxLength(int[] nums) {
   
    Map<Integer, Integer> sumToIndex = new HashMap();
    sumToIndex.put(0, -1);
    int sum = 0;
    int maxLength = 0;
    for (int i = 0; i < nums.length; ++i) {
   
        sum += nums[i] == 0 ? -1 : 1;
        if (sumToIndex.containsKey(sum)) {
   
            maxLength = Math.max(maxLength, i - sumToIndex.get(sum));
        } else {
   
            sumToIndex.put(sum, i);
        }
    }

    return maxLength;
}

面试题12:左右两边子数组的和相等

题目

输入一个整数数组,如果一个数字左边的子数组数字之和等于右边的子数组数字之和,请返回该数字的下标。如果存在多个这样的数字,则返回最左边一个的下标。如果不存在这样的数字,则返回-1。例如在数组1, 7, 3, 6, 2, 9]中,下标为3的数字(值为6)左边三个数字1、7、3和右边两个数字29的和相等,都是11,因此正确的输出值是3

参考代码

public int pivotIndex(int[] nums) {
   
    int total = 0;
    for (int num : nums) {
   
        total += num;
    }

    int sum = 0;
    for (int i = 0; i < nums.length; ++i) {
   
        sum += nums[i];
        if (sum - nums[i] == total - sum) {
   
            return i;
        }
    }

    return -1;
}

面试题13:二维子矩阵的和

题目

输入一个二维矩阵,如何计算给定左上角坐标和右下角坐标的子矩阵数字之和?对同一个二维矩阵,计算子矩阵数字之和的函数可能输入不同的坐标而被反复调用多次。例如输入图2.1中的二维矩阵,以及左上角坐标为(2, 1)和右下角坐标为(4, 3),该函数输出8

《剑指offer》专项突破,java,算法,数据结构
图2.1:在一个5×5的二维数组中左上角坐标为(2, 1)、右下角坐标为(4, 3)的子矩阵(有灰色背景部分)的和等于8

参考代码

class NumMatrix {
   
    private int[][] sums;
 
    public NumMatrix(int[][] matrix) {
   
        if (matrix.length == 0 || matrix[0].length == 0) {
   
            return;
        }
        
        sums = new int[matrix.length + 1][matrix[0].length + 1];
        for (int i = 0; i < matrix.length; ++i) {
   
            int rowSum = 0;
            for (int j = 0; j < matrix[0].length; ++j) {
   
                rowSum += matrix[i][j];
                sums[i + 1][j + 1] = sums[i][j + 1] + rowSum;
            }
        }
    }
    
    public int sumRegion(int row1, int col1, int row2, int col2) {
   
        return sums[row2 + 1][col2 + 1] - sums[row1][col2 + 1]
            - sums[row2 + 1][col1] + sums[row1][col1]; 
    }
}

第三章:字符串

面试题14:字符串中的变位词

题目

输入两个字符串s1s2,如何判断s2中是否包含s1的某个变位词?如果s2中包含s1的某个变位词,则s1至少有一个变位词是s2的子字符串。假设两个输入字符串中只包含英语小写字母。例如输入字符串s1为"ab",s2为"dgcaf",由于s2中包含s1的变位词"ba",因此输出是true。如果输入字符串s1为"ac",s2为"dcgaf",输出为false

参考代码

public boolean checkInclusion(String s1, String s2) {
   
    if (s2.length() >= s1.length()) {
   
        int[] counts = new int[26];
        for (int i = 0; i < s1.length(); ++i) {
   
            counts[s1.charAt(i) - 'a']++;
            counts[s2.charAt(i) - 'a']--;
        }

        if (areAllZero(counts)) {
   
            return true;
        }

        for (int i = s1.length(); i < s2.length(); ++i) {
   
            counts[s2.charAt(i) - 'a']--;
            counts[s2.charAt(i - s1.length()) - 'a']++;
            if (areAllZero(counts)) {
   
                return true;
            }
        }
    }

    return false;
}

private boolean areAllZero(int[] counts) {
   
    for (int count : counts) {
   
        if (count != 0) {
   
            return false;
        }
    }

    return true;
}

面试题15:字符串中的所有变位词

题目

输入两个字符串s1s2,如何找出s2的所有变位词在s1中的起始下标?假设两个输入字符串中只包含英语小写字母。例如输入字符串s1为"cbadabacg",s2为"abc",s2有两个变位词"cba"和"bac"是s1中的字符串,输出它们在s1中的起始下标05

参考代码

    public List<Integer> findAnagrams(String s1, String s2) {
   
        List<Integer> indices = new LinkedList<>();
        if (s1.length() >= s2.length()) {
   
            int[] counts = new int[26];
            for (int i = 0; i < s2.length(); ++i) {
   
                counts[s2.charAt(i) - 'a']++;
                counts[s1.charAt(i) - 'a']--;
            }

            if (areAllZero(counts)) {
   
                indices.add(0);
            }

            for (int i = s2.length(); i < s1.length(); ++i) {
   
                counts[s1.charAt(i) - 'a']--;
                counts[s1.charAt(i - s2.length()) - 'a']++;
                if (areAllZero(counts)) {
   
                    indices.add(i - s2.length() + 1);
                }
            }
        }
        
        return indices;
    }
    
    private boolean areAllZero(int[] counts) {
   
        for (int count : counts) {
   
            if (count != 0) {
   
                return false;
            }
        }
        
        return true;
    }

面试题16:不含重复字符的最长子字符串

题目

输入一个字符串,求该字符串中不含重复字符的最长连续子字符串的长度。例如,输入字符串"babcca",它最长的不含重复字符串的子字符串是"abc",长度为3

参考代码

解法一
public int lengthOfLongestSubstring(String s) {
   
    int[] counts = new int[256];
    int longest = s.length() > 0 ? 1 : 0;
    for (int i = 0, j = -1; i < s.length(); ++i) {
   
        counts[s.charAt(i)]++;
        while (hasGreaterThan1(counts)) {
   
            ++j;
            counts[s.charAt(j)]--;
        }

        longest = Math.max(i - j, longest);
    }

    return longest;
}

private boolean hasGreaterThan1(int[] counts) {
   
    for (int count : counts) {
   
        if (count > 1) {
   
            return true;
        }
    }

    return false;
}    
解法二
public int lengthOfLongestSubstring(String s) {
   
    int[] counts = new int[256];
    int longest = s.length() > 0 ? 1 : 0;
    int countDup = 0;
    for (int i = 0, j = -1; i < s.length(); ++i) {
   
        counts[s.charAt(i)]++;
        if (counts[s.charAt(i)] == 2) {
   
            countDup++;
        }

        while (countDup > 0) {
   
            ++j;
            counts[s.charAt(j)]--;
            if (counts[s.charAt(j)] == 1) {
   
                countDup--;
            }
        }

        longest = Math.max(i - j, longest);
    }

    return longest;
}

面试题17:含有所有字符的最短字符串

题目

输入两个字符串st,请找出s中包含t的所有字符的最短子字符串。例如输入s为字符串"ADDBANCAD",t为字符串"ABC",则s中包含字符’A’、‘B’、'C’的最短子字符串是"BANC"。如果不存在符合条件的子字符串,返回空字符串""。如果存在多个符合条件的子字符串,返回任意一个

参考代码

public String minWindow(String s, String t) {
   
    HashMap<Character, Integer> charToCount = new HashMap<>();
    for (char ch : t.toCharArray()) {
   
        charToCount.put(ch, charToCount.getOrDefault(ch, 0) + 1);
    }

    int count = charToCount.size();
    int start = 0, end = 0, minStart = 0, minEnd = 0;
    int minLength = Integer.MAX_VALUE;
    while (end < s.length() || (count == 0 && end == s.length())) {
   
        if (count > 0) {
   
            char endCh = s.charAt(end);
            if (charToCount.containsKey(endCh)) {
   
                charToCount.put(endCh, charToCount.get(endCh) - 1);
                if (charToCount.get(endCh) == 0) {
   
                    count--;
                }
            }

            end++;
        } else {
   
            if (end - start < minLength) {
   
                minLength = end - start;
                minStart = start;
                minEnd = end;
            }

            char startCh = s.charAt(start);
            if (charToCount.containsKey(startCh)) {
   
                charToCount.put(startCh, charToCount.get(startCh) + 1);
                if (charToCount.get(startCh) == 1) {
   
                    count++;
                }
            }

            start++;
        }
    }

    return minLength < Integer.MAX_VALUE
        ? s.substring(minStart, minEnd)
        : "";
}

面试题18:有效的回文

题目

给定一个字符串,请判断它是不是一个回文字符串。我们只需要考虑字母或者数字字符,并忽略大小写。例如,"A man, a plan, a canal: Panama"是一个回文字符串,而"race a car"不是。

参考代码

public boolean isPalindrome(String s) {
   
    int i = 0;
    int j = s.length() - 1;
    while (i < j) {
   
        char ch1 = s.charAt(i);
        char ch2 = s.charAt(j);
        if (!Character.isLetterOrDigit(ch1)) {
   
            i++;
        } else if (!Character.isLetterOrDigit(ch2)) {
   
            j--;
        } else {
   
            ch1 = Character.toLowerCase(ch1);
            ch2 = Character.toLowerCase(ch2);
            if (ch1 != ch2) {
   
                return false;
            }

            i++;
            j--;
        }       
    }

    return true;
}

面试题19:最多删除一个字符得到回文

题目

给定一个字符串,请判断如果最多从字符串中删除一个字符能不能得到一个回文字符串。例如,如果输入字符串"abca",由于删除字符’b’或者’c’就能得到一个回文字符串,因此输出为true

参考代码

public boolean validPalindrome(String s) {
   
    int start = 0;
    int end = s.length() - 1;
    for (; start < s.length() / 2; ++start, --end) {
   
        if (s.charAt(start) != s.charAt(end)) {
   
            break;
        }
    }

    return start == s.length() / 2
        || isPalindrome(s, start, end - 1)
        || isPalindrome(s, start + 1, end);
}

private boolean isPalindrome(String s, int start, int end) {
   
    while (start < end) {
   
        if (s.charAt(start) != s.charAt(end)) {
   
            break;
        }

        start++;
        end--;
    }

    return start >= end;
}

面试题20:回文子字符串的个数

题目

给定一个字符串,请问字符串里有多少回文连续子字符串?例如,字符串里"abc"有3个回文字符串,分别为"a"、“b”、“c”;而字符串"aaa"里有6个回文子字符串,分别为"a"、“a”、“a”、“aa”、“aa"和"aaa”。

参考代码

    public int countSubstrings(String s) {
   
        if (s == null || s.length() == 0) {
   
            return 0;
        }
        
        int count = 0;
        for (int i = 0; i < s.length(); ++i) {
   
            count += countPalindrome(s, i, i);
            count += countPalindrome(s, i, i + 1);
        }
        
        return count;
    }
    
    private int countPalindrome(String s, int start, int end) {
   
        int count = 0;
        while (start >= 0 && end < s.length()
               && s.charAt(start) == s.charAt(end)) {
   
            count++;
            start--;
            end++;
        }
        
        return count;
    }

第四章:链表

面试题21:删除倒数第k个结点

题目

给你一个链表,请问如何删除链表中的倒数第k个结点?假设链表中结点的总数为n,那么1≤k≤n。要求只能遍历链表一次
例如输入图4.1中(a)的链表,删除倒数第2个结点之后的链表如图4.1中(b)所示。

《剑指offer》专项突破,java,算法,数据结构

图4.1:从链表中删除倒数第2个结点。(a)一个包含6个结点的链表。(b)删除倒数第2个结点(值为5的结点)之后的链表。

参考代码

public ListNode removeNthFromEnd(ListNode head, int n) {
   
    ListNode dummy = new ListNode(0);
    dummy.next = head;

    ListNode front = head, back = dummy;
    for (int i = 0; i < n; i++) {
   
        front = front.next;
    }

    while (front != null) {
   
        front = front.next;
        back = back.next;
    }

    back.next = back.next.next;                    
    return dummy.next;
}

面试题22:链表中环的入口结点

题目

一个链表中包含环,如何找出环的入口结点?从链表的头结点开始沿着next指针进入环的第一个结点为环的入口结点。例如,在图4.3的链表中,环的入口结点是结点3

《剑指offer》专项突破,java,算法,数据结构

图4.3:结点3是链表中环的入口结点

参考代码

public ListNode detectCycle(ListNode head) {
   
    ListNode inLoop = getNodeInLoop(head);
    if (inLoop == null) {
   
        return null;
    }

    ListNode node = head;
    while (node != inLoop) {
   
        node = node.next;
        inLoop = inLoop.next;
    }

    return node;
}

private ListNode getNodeInLoop(ListNode head) {
   
    if (head == null || head.next == null) {
   
        return null;
    }

    ListNode slow = head.next;
    ListNode fast = slow.next;
    while (slow != null && fast != null) {
   
        if (slow == fast)
            return slow;

        slow = slow.next;
        fast = fast.next;
        if (fast != null)
            fast = fast.next;
    }

    return null;
}

面试题23:两个链表的第一个重合结点

题目

输入两个单向链表,请问如何找出它们的第一个重合结点。例如图4.5中的两个链表的第一个重合的结点的值是4

《剑指offer》专项突破,java,算法,数据结构
图4.5:两个部分重合的链表,它们的第一个重合的结点的值是4

参考代码

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
   
    int count1 = countList(headA);
    int count2 = countList(headB);
    int delta = Math.abs(count1 - count2);
    ListNode longer = count1 > count2 ? headA : headB;
    ListNode shorter = count1 > count2 ? headB : headA;
    ListNode node1 = longer;
    for (int i = 0; i < delta; ++i) {
   
        node1 = node1.next;
    }

    ListNode node2 = shorter;
    while (node1 != node2) {
   
        node2 = node2.next;
        node1 = node1.next;
    }

    return node1;
}

private int countList(ListNode head) {
   
    int count = 0;
    while (head != null) {
   
        count++;
        head = head.next;
    }

    return count;
}

面试题24:反转链表

题目

定义一个函数,输入一个链表的头结点反转该链表并输出反转后链表的头结点。例如,把图4.8(a)中的链表反转之后得到的链表如图4.8(b)所示。

《剑指offer》专项突破,java,算法,数据结构

图4.8:反转一个链表。(a)一个含有5个结点的链表。(b)反转之后的链表。

参考代码

public ListNode reverseList(ListNode head) {
   
    ListNode prev = null;
    ListNode cur = head;    
    while (cur != null) {
   
        ListNode next = cur.next;
        cur.next = prev;
        prev = cur;
        cur = next;
    }

    return prev;
}

面试题25:链表中的数字相加

题目

给你两个表示非负整数的单向链表,请问如何实现这两个整数的相加并且把和仍然用单向链表表示?链表中的每个结点表示整数十进制的一位,并且头结点对应整数的最高位数而尾结点对应整数的个位数。例如在图4.10(a)和(b)中的两个链表分别表示整数123531,它们的和为654,对应的链表尾图4.10的(c)所示。

《剑指offer》专项突破,java,算法,数据结构
图4.10:链表中数字以及它们的和。(a)表示整数123的链表。(b)表示整数531的链表。(c)表示123531的和654的链表。

参考代码

public ListNode addTwoNumbers(ListNode head1, ListNode head2) {
   
    head1 = reverseList(head1);
    head2 = reverseList(head2);
    ListNode reversedHead = addReversed(head1, head2);
    return reverseList(reversedHead);
}

private ListNode addReversed(ListNode head1, ListNode head2) {
   
    ListNode dummy = new ListNode(0);
    ListNode sumNode = dummy;
    int carry = 0;
    while (head1 != null || head2 != null) {
   
        int sum = (head1 == null ? 0 : head1.val)
                + (head2 == null ? 0 : head2.val) + carry;
        carry = sum >= 10 ? 1 : 0;
        sum = sum >= 10 ? sum - 10 : sum;        
        ListNode newNode = new ListNode(sum);

        sumNode.next = newNode;            
        sumNode = sumNode.next;

        head1 = head1 == null ? null : head1.next;
        head2 = head2 == null ? null : head2.next;
    }

    if (carry > 0) {
   
        sumNode.next = new ListNode(carry);
    }

    return dummy.next;
}

private ListNode reverseList(ListNode head) {
   
    ListNode reversedHead = null;
    ListNode prev = null;
    ListNode cur = head;    
    while (cur != null) {
   
        ListNode next = cur.next;
        if (next == null)
            reversedHead = cur;

        cur.next = prev;
        prev = cur;
        cur = next;
    }

    return reversedHead;
}

面试题26:重排链表

题目

给你一个链表,链表中结点的顺序是L0→ L1→ L2→…→ Ln-1→ Ln,请问如何重排链表使得结点的顺序变成L0→ Ln→ L1→ Ln-1→ L2→ Ln-2→…?例如输入图4.12(a)中的链表,重排之后的链表如图4.12(b)所示。

《剑指offer》专项突破,java,算法,数据结构
图4.12:重排链表。(a)一个含有6个结点的链表。(b)重排之后的链表。

参考代码

public void reorderList(ListNode head) {
   
    ListNode dummy = new ListNode(0);
    dummy.next = head;        
    ListNode fast = dummy;
    ListNode slow = dummy;
    while (fast != null && fast.next != null) {
   
        slow = slow.next;
        fast = fast.next;
        if (fast.next != null) {
   
            fast = fast.next;
        }            
    }

    ListNode temp = slow.next;
    slow.next = null;
    link(head, reverseList(temp), dummy);
}

private void link(ListNode node1, ListNode node2, ListNode head) {
   
    ListNode prev = head;
    while (node1 != null && node2 != null) {
   
        ListNode temp = node1.next;

        prev.next = node1;
        node1.next = node2;
        prev = node2;

        node1 = temp;
        node2 = node2.next;
    }

    if (node1 != null) {
   
        prev.next = node1;
    }
}

private ListNode reverseList(ListNode first) {
   
    ListNode prev = null;
    ListNode cur = first;
    ListNode head = null;
    while (cur != null) {
   
        ListNode next = cur.next;
        cur.next = prev;
        if (next == null) {
   
            head = cur;
        }

        prev = cur;
        cur = next;
    }

    return head;
}

面试题27:回文链表

题目

如何判断一个链表是不是回文?要求解法的时间复杂度O(n),另外不得使用超过O(1)辅助空间。如果一个链表是回文,那么链表中结点序列从前往后看和从后往前看是相同的。例如,图4.13中的链表的结点序列从前往后看和从后往前看都是1、2、3、3、2、1,因此这是一个回文链表。

《剑指offer》专项突破,java,算法,数据结构
图4.13:一个回文链表。

参考代码

public boolean isPalindrome(ListNode head) {
   
    if (head == null || head.next == null) {
   
        return true;
    }

    ListNode slow = head;
    ListNode fast = head.next;
    while (fast.next != null && fast.next.next != null) {
   
        fast = fast.next.next;
        slow = slow.next;
    }

    ListNode secondHalf = slow.next;
    if (fast.next != null) {
   
        secondHalf = slow.next.next;
    }

    slow.next = null;
    return equals(secondHalf, reverseList(head));
}

public ListNode reverseList(ListNode head) {
   
    ListNode reversedHead = null;
    ListNode prev = null;
    ListNode cur = head;    
    while (cur != null) {
   
        ListNode next = cur.next;
        if (next == null) {
   
            reversedHead = cur;
        }

        cur.next = prev;
        prev = cur;
        cur = next;
    }

    return reversedHead;
}

面试题28:展平多级双向链表

题目

在一个多级双向链表中节点除了有两个指针分别指向前后两个节点之外,还有一个指针指向它的子链表,并且子链表也是一个双向链表,它的节点也有指向子链表的指针。请将这样的多级双向链表展平成普通的双向链表,即所有节点都没有子链表。例如图4.14(a)是一个多级双向链表,它展平之后如图4.14(b)所示。

《剑指offer》专项突破,java,算法,数据结构
图4.14:展平多级双向链表。(a)一个多级双向链表。(b)展平之后的双向链表。文章来源地址https://www.toymoban.com/news/detail-789173.html

参考代码

public Node flatten(Node head) {
   
    flattenGetTail(head);
    return head;
}

private Node flattenGetTail(Node head) {
   
    Node node = head;
    Node tail = null;
    while (node != null) {
   
        Node next = node.next;
        if (node.child != null) {
   
            Node child = node.child;
            Node childTail = flattenGetTail(node.child);

            node.child = null;
            node.next = child;
            child.prev = node;
            childTail.next = next;
            

到了这里,关于《剑指offer》专项突破的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 《剑指 Offer》专项突破版 - 面试题 15 : 字符串中的所有变位词(C++ 实现)

    题目链接 :LCR 015. 找到字符串中所有字母异位词 - 力扣(LeetCode) 题目 : 输入字符串 s1 和 s2,如何找出字符串 s2 的所有变位词在字符串 s1 中的起始下标?假设两个字符串中只包含英文小写字母。例如,字符串 s1 为 \\\"cbadabacg\\\",字符串 s2 为 \\\"abc\\\",字符串 s2 的两个变位词 \\\"c

    2024年01月18日
    浏览(65)
  • 《剑指 Offer》专项突破版 - 面试题 13 : 二维子矩阵的数字之和(C++ 实现)- 二维前缀和

    题目链接 :LCR 013. 二维区域和检索 - 矩阵不可变 - 力扣(LeetCode) 题目 : 输入一个二维矩阵,如何计算给定左上角坐标和右下角坐标的子矩阵的数字之和?对于同一个二维矩阵,计算子矩阵的数字之和的函数可能由于输入不同的坐标而反复调用多次。 例如,对于下图中的二

    2024年01月18日
    浏览(39)
  • 【剑指offer】数据结构——数组

    【剑指offer】03.数组中重复的数字 //03. 数组中重复的数字 // 找出数组中重复的数字。 // 力扣 // 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。 // 数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每 // 个数字重复了几次。请找出数组中任意一

    2024年02月08日
    浏览(36)
  • 二叉树(中)+Leetcode每日一题——“数据结构与算法”“剑指Offer55-I. 二叉树的深度”“100.相同的树”“965.单值二叉树”

    各位CSDN的uu们你们好呀,今天继续数据结构与算法专栏中的二叉树,下面,让我们进入二叉树的世界吧!!! 二叉树(上)——“数据结构与算法”_认真学习的小雅兰.的博客-CSDN博客 二叉树链式结构的实现 二叉树链式结构的实现 求二叉树的高度 但是这种写法有很大的问题

    2024年02月17日
    浏览(37)
  • 【数据结构入门精讲 | 第九篇】考研408排序算法专项练习(一)

    前面几篇文章介绍的是排序算法,现在让我们开始排序算法的专项练习。 1.希尔排序是稳定的算法。(错) 解析:稳定性是指如果两个元素在排序前后的相对顺序保持不变,那么这个排序算法就是稳定的。对于具有相同的元素,排序后它们的相对位置应该保持不变。

    2024年02月03日
    浏览(46)
  • java入门,程序=数据结构+算法

    一、前言 在学习java的时候,我印象最深的一句话是:程序=数据结构+算法,对于写java程序来说,这就是java的入门。 二、java基本数据结构与算法 1、数据类型 java中的数据类型8种基本数据类型: 整型 byte 、short 、int 、long 浮点型 float 、 double 字符型 char 布尔型 boolean 还有包

    2024年02月05日
    浏览(59)
  • Java 数据结构与算法-树

    树的基础知识 树是算法面试经常遇到的数据结构之一,在实际工作中也有可能经常用到…… 应聘者在准备算法面试时最需要重视的是二叉树…… 二叉树是一种典型的具有递归性质的数据结构。二叉树的根节点可能有子节点,子节点又是对应子树的根节点,它可能也有自己的

    2024年02月08日
    浏览(51)
  • java数据结构与算法:栈

    代码: 测试: 链表头为堆栈顶 代码: 测试:

    2024年01月21日
    浏览(50)
  • 【LeetCode】剑指 Offer 41. 数据流中的中位数 p214 -- Java Version

    题目链接 :https://leetcode.cn/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

    2023年04月08日
    浏览(54)
  • Java数据结构与算法:查找算法之二分查找

    大家好,我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编,欢迎回到本专栏。在这个冰冷的季节里,我们将一同探讨Java中一种高效的查找算法——二分查找。让我们点燃知识的火花,一同解锁这个查找奇迹的秘密! 二分查找简介 二分查找,也称为折半查找,

    2024年01月21日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包