Top100题(上)

Top100(上)

散列

1. 两数之和

struct MyListNode {
    int val;
    int pos;
    struct MyListNode *next;
};

// 散列表
typedef struct {
    struct MyListNode *data;
} MyHashMap;

const int hashSize = 1543;

MyHashMap *createHashMap() {
    MyHashMap *hashMap = (MyHashMap *) malloc(sizeof(MyHashMap));
    hashMap->data = (struct MyListNode *) malloc(sizeof(struct MyListNode) * hashSize);
    for (int i = 0; i < hashSize; ++i) {
        hashMap->data[i].val = 0x80000000;
        hashMap->data[i].pos = -1;
        hashMap->data[i].next = NULL;
    }
    return hashMap;
}

int hash(int key) {
    return (key+1000000000) % hashSize;
}

struct MyListNode *getList(MyHashMap *hashMap, int key) {
    return &(hashMap->data[hash(key)]);
}

int containsKey(MyHashMap *hashMap, int key) {
    struct MyListNode *head = getList(hashMap, key);
    while (head != NULL) {
        if (head->val == key) return head->pos;
        head = head->next;
    }
    return -1;
}

void insert(MyHashMap *hashMap, int key, int pos) {
    if (containsKey(hashMap, key) != -1)return;
    struct MyListNode *head = getList(hashMap, key);
    struct MyListNode *node = (struct MyListNode *) malloc(sizeof(struct MyListNode));
    node->val = key;
    node->pos = pos;
    node->next = head->next;
    head->next = node;
}


int *twoSum(int *nums, int numsSize, int target, int *returnSize) {
    int *res = (int *) malloc(sizeof(int) * 2);
    *returnSize = 0;
    MyHashMap *hashMap = (MyHashMap *) malloc(sizeof(MyHashMap));
    hashMap = createHashMap();

    for (int i = 0; i < numsSize; ++i) {
        int t = containsKey(hashMap, target - nums[i]);
        if (t != -1) {
            res[0] = i;
            res[1] = t;
            *returnSize = 2;
            return res;
        } else {
            insert(hashMap, nums[i], i);
        }
    }
    return res;
}

49. 字母异位词分组

// todo
// 字符串排序后放入hashmap
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<String, List<String>>();
        for (String str : strs) {
            // 字符串保存到字符数组
            char[] chars = str.toCharArray();
            // 对字符数组排序
            Arrays.sort(chars);
            String key = new String(chars);
            List<String> list = map.getOrDefault(key, new ArrayList<String>());
            // 把原始的字符串存入map中对应的list
            list.add(str);
            map.put(key, list);
        }
        return new ArrayList<List<String>>(map.values());
    }
}
// 将每个出现次数大于0的字母和出现次数按顺序拼接成字符串,作为哈希表的键
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<String, List<String>>();
        for (String str : strs) {
            int[] counts = new int[26];
            int length = str.length();
            // 统计字符串中每个字母出现的次数
            for (int i = 0; i < length; i++) {
                counts[str.charAt(i) - 'a']++;
            }
            StringBuilder sb = new StringBuilder();
            // 把出现的字符和出现的字数追加到key中
            for (int i = 0; i < 26; i++) {
                if (counts[i] != 0) {
                    sb.append((char) ('a' + i));
                    sb.append(counts[i]);
                }
            }
            String key = sb.toString();
            List<String> list = map.getOrDefault(key, new ArrayList<String>());
            list.add(str);
            map.put(key, list);
        }
        return new ArrayList<List<String>>(map.values());
    }
}
struct Node {
    char *value;
    struct Node *next;
};

struct DummyNode {
    // 记录排序后的字符串
    char *sortedStr;
    // 记录元素个数
    int count;
    struct Node *next;
};

typedef struct {
    struct DummyNode *data;
    // 当前有多少组已有元素
    int groupCount;
    int hashSize;
} HashMap;

int cmp(const void *a, const void *b) {
    return *(char *) a - *(char *) b;
}

HashMap *createHashMap(int strsSize) {
    HashMap *hashMap = (HashMap *) malloc(sizeof(HashMap));
    // 组数最多也就是字符串的个数
    hashMap->hashSize = strsSize;
    hashMap->groupCount = 0;
    // 为数组分配空间
    hashMap->data = (struct DummyNode *) malloc(sizeof(struct DummyNode) * strsSize);
    // 初始化hashMap,为每一组的虚拟头节点赋值
    for (int i = 0; i < strsSize; ++i) {
        hashMap->data[i].sortedStr = "";
        hashMap->data[i].count = 0;
        hashMap->data[i].next = NULL;
    }
    return hashMap;
}

// todo 可以优化查找
struct DummyNode *findNodeList(HashMap *hashMap, char *sorted) {
    // 遍历所有非空组
    for (int i = 0; i < hashMap->groupCount; ++i)
        // 如果已经存在这一组,就返回该组的虚拟头节点
        if (strcmp(hashMap->data[i].sortedStr, sorted) == 0)
            return &(hashMap->data[i]);
    // 不存在该组,则组数加一,返回当前组虚拟头节点
    hashMap->data[hashMap->groupCount].sortedStr = (char *) malloc(sizeof(char) * (strlen(sorted) + 1));
    strcpy(hashMap->data[hashMap->groupCount].sortedStr, sorted);
    // *错误写法:hashMap->data[hashMap->groupCount].sortedStr = sorted;
    hashMap->groupCount++;
    return &(hashMap->data[hashMap->groupCount - 1]);
}

void addToHashMap(HashMap *hashMap, char *str) {
    // 把字符串存入缓存,并把缓存中的字符排序
    char buff[strlen(str) + 1];
    strcpy(buff, str);
    // strlen不要加一
    qsort(buff, strlen(buff), sizeof(char), cmp);
    // 头插法插入对应的组
    struct DummyNode *dummyHead = findNodeList(hashMap, buff);
    struct Node *node = (struct Node *) malloc(sizeof(struct Node));
    node->value = str;
    node->next = dummyHead->next;
    dummyHead->next = node;
    // 当前组元素个数加一
    dummyHead->count++;
}

// 排序后分组
char ***groupAnagrams(char **strs, int strsSize, int *returnSize, int **returnColumnSizes) {
    *returnSize = 0;
    if (strsSize <= 0 || strs == NULL) return NULL;
    HashMap *hashMap = createHashMap(strsSize);
    // 记录到hashMap
    for (int i = 0; i < strsSize; ++i)
        addToHashMap(hashMap, strs[i]);

    int groupCount = hashMap->groupCount;
    *returnSize = groupCount;
    char ***res = (char ***) malloc(sizeof(char **) * groupCount);
    *returnColumnSizes = (int *) calloc(groupCount, sizeof(int));
    for (int i = 0; i < groupCount; ++i) {
        int len = hashMap->data[i].count;
        // 记录当前组元素总数
        (*returnColumnSizes)[i] = len;
        res[i] = (char **) malloc(sizeof(char *) * len);
        struct Node *cur = hashMap->data[i].next;
        for (int j = 0; j < len; ++j) {
            res[i][j] = cur->value;
            cur = cur->next;
        }
    }

    return res;
}

128. 最长连续序列

int cmp(const void *a, const void *b) {
    return (*(int *) a) - (*(int *) b);
}

// 时间复杂度O(nlogn)
int longestConsecutive(int *nums, int numsSize) {
    qsort(nums, numsSize, sizeof(int), cmp);
    int maxLen = 0, tempLen = 0;
    for (int i = 0; i < numsSize; ++i) {
        if (i == 0) {
            tempLen++;
            maxLen = 1;
        } else {
            if (nums[i] - nums[i - 1] == 0) continue;
            if (nums[i] - nums[i - 1] == 1) {
                tempLen++;
                if (tempLen > maxLen) maxLen = tempLen;
            } else {
                // 当前值不是前一个值加1,则这个值是一个新序列的开头
                tempLen = 1;
            }
        }
    }
    return maxLen;
}
class Solution {
    // 时间复杂度O(n)
    public int longestConsecutive(int[] nums) {
        Set<Integer> set = new HashSet<>();
        // 加入集合,对数组去重
        for (int num : nums) set.add(num);

        int maxLen = 0;
        for (Integer num : set) {
            // 先检查上个元素是否出现在set中,如果有就不用重复统计tempLen了
            if (!set.contains(num - 1)) {
                // 如果上个元素不在set中,则这个元素就是这个序列的首个元素
                int tempLen = 1;
                int curNum = num + 1;
                // 依次判断后续一个元素是否在set中
                while (set.contains(curNum)) {
                    tempLen++;
                    curNum++;
                }
                maxLen = Math.max(tempLen, maxLen);
            }
        }
        return maxLen;
    }
}

双指针

283. 移动零

void moveZeroes(int *nums, int numsSize) {
    // slow指向当前应该存放非零元素的位置
    int slow = 0;
    // fast遇到非零元素时,就把元素移到nums[slow]中
    int fast = 0;
    while (fast < numsSize) {
        if (nums[fast] != 0) {
            nums[slow++] = nums[fast];
        }
        fast++;
    }
    // slow及其后面的都是0
    while (slow < numsSize) {
        nums[slow++] = 0;
    }
}

11. 盛最多水的容器

int min(int a, int b) {
    return a < b ? a : b;
}

int max(int a, int b) {
    return a > b ? a : b;
}
// 暴力枚举
int maxArea(int *height, int heightSize) {
    int res = 0;
    for (int i = 0; i < heightSize; ++i) {
        for (int j = i + 1; j < heightSize; ++j) {
            int temp = (j - i) * min(height[i], height[j]);
            res = max(res, temp);
        }
    }
    return res;
}

int min(int a, int b) {
    return a < b ? a : b;
}

int max(int a, int b) {
    return a > b ? a : b;
}

int maxArea(int *height, int heightSize) {
    int left = 0, right = heightSize - 1;
    int res = 0;
    while (left < right) {
        int temp = (right - left) * min(height[left], height[right]);
        res = max(res, temp);
        // 每次把短板往里移动,短板可能变长,总面积才可能变大
        // 如果移动长板,底一定变小,高度不会超过之前的那个短板,高只会原来越低,面积只会变小
        if (height[left] < height[right])
            left++;
        else
            right--;
    }
    return res;
}

15. 三数之和

int cmp(const void *a, const void *b) {
    return (*(int *) a) - (*(int *) b);
}

// 三元组最大个数
const int SIZE = 20000;

int **threeSum(int *nums, int numsSize, int *returnSize, int **returnColumnSizes) {
    if (nums == NULL || numsSize < 3) return NULL;
    int **res = (int **) malloc(sizeof(int *) * SIZE);
    int index = 0;
    *returnColumnSizes = (int *) malloc(sizeof(int) * SIZE);
    *returnSize = 0;

    // 排序
    qsort(nums, numsSize, sizeof(int), cmp);

    // 排序后最后一个数小于0时,不符合题意
    if (nums[numsSize - 1] < 0) return NULL;

    for (int i = 0; i < numsSize - 2; ++i) {
        // 当前值都大于0了,后面就没必要找了
        if (nums[i] > 0) break;
        if (i > 0) {
            // 上一个固定i的情况已经讨论完了,包括nums[left],nums[right]也等于nums[i]的情况
            // i必须更新到下一个值不同的地方
            while ((i < numsSize - 2) && nums[i] == nums[i - 1]) i++;
        }
        int left = i + 1, right = numsSize - 1;
        while (left < right) {
            int sum = nums[i] + nums[left] + nums[right];
            if (sum == 0) {
                res[index] = (int *) malloc(sizeof(int) * 3);
                res[index][0] = nums[i];
                res[index][1] = nums[left];
                res[index][2] = nums[right];
                index++;
                // 当前的left,right,i是符合要求的三元组
                // 固定i,同时变动left,right使nums[left],nums[right]都变成新的值
                // 若只变动left,right中的一个,那这三个数一定不符合题意,因为原来的三个刚好符合题意
                // 把left移到与当前值相同的最后一个位置
                while (left < right && nums[left] == nums[left + 1]) left++;
                // 把left移动到和当前值不同的第一个位置
                left++;
                while (left < right && nums[right] == nums[right - 1]) right--;
                right--;
                // left,right找完了,跳出循环,修改i
                if (left >= right) break;
            } else if (sum > 0) {
                // right移到比当前值小的下一个元素
                while (left < right && nums[right] == nums[right - 1]) right--;
                right--;
            } else {
                // left移到比当前值大的下一个元素
                while (left < right && nums[left] == nums[left + 1]) left++;
                left++;
            }
        }
    }

    *returnSize = index;
    *returnColumnSizes = (int *) malloc(sizeof(int) * index);
    for (int i = 0; i < index; ++i) {
        (*returnColumnSizes)[i] = 3;
    }

    return res;
}

42. 接雨水

// 按行累积,每次累加当前行上能接多少水(超时)
int trap(int *height, int heightSize) {
    int res = 0;
    int lever = 1;
    // 找最大高度
    int maxHeight = 0;
    for (int i = 0; i < heightSize; ++i)
        if (height[i] > maxHeight) maxHeight = height[i];

    // 每次找一层,一格一格的加
    while (lever <= maxHeight) {
        int i = 0;
        // 找到第一个不低于当前lever的作为左边界
        while (i < heightSize && height[i] < lever) i++;
        if (i >= heightSize) continue;
        int temp = 0;
        while (i < heightSize) {
            if (height[i] < lever) {
                // 已有左边界,并且比当前层低,说明这个格子(i, lever)可以放水
                temp++;
            } else if (height[i] >= lever) {
                // 找到大于或等于当前层的右边界,就把之前累积的水加到结果中,并清空temp
                // 当前的右边界变成下一个左边界,在继续寻找下一个右边界
                res += temp;
                temp = 0;
            }
            i++;
        }
        lever++;
    }
    return res;
}
int findMax(int *height, int start, int end) {
    int max = height[start];
    while (start <= end) {
        if (height[start] > max) max = height[start];
        start++;
    }
    return max;
}

// 按列累积,每次累加当前列上能接多少水
int trap(int *height, int heightSize) {
    int res = 0;

    // 记录当前元素左边的最大值
    int tempLeftMax = height[0];
    int leftMaxArr[heightSize];
    for (int i = 1; i <= heightSize - 2; ++i) {
        leftMaxArr[i] = tempLeftMax;
        if (height[i] > tempLeftMax) tempLeftMax = height[i];
    }

    // 记录当前元素右边的最大值
    int tempRightMax = height[heightSize - 1];
    int rightMaxArr[heightSize];
    for (int i = heightSize - 2; i >= 1; i--) {
        rightMaxArr[i] = tempRightMax;
        if (height[i] > tempRightMax) tempRightMax = height[i];
    }

    for (int i = 1; i < heightSize - 1; ++i) {
        // 找左右两边最高的列
        // 1.大量重复寻找会超时
//        int leftMax = findMax(height, 0, i - 1);
//        int rightMax = findMax(height, i + 1, heightSize - 1);
        // 2.两边遍历保存结果
        int leftMax = leftMaxArr[i];
        int rightMax = rightMaxArr[i];
        int min = leftMax < rightMax ? leftMax : rightMax;
        // 只有左边最高的和右边最高的,二者中的较小者比当年的列高,当前列才能接得住水
        // 较小者小于等于当前列,接不住水
        if (min > height[i]) res += min - height[i];
    }
    return res;
}
// 用leftMax优化掉leftMaxArr数组
// 由于i是从左往右,所以rightMaxArr没法用这个办法优化掉
int trap(int *height, int heightSize) {
    int res = 0;  

    // 记录当前元素右边的最大值
    int tempRightMax = height[heightSize - 1];
    int rightMaxArr[heightSize];
    for (int i = heightSize - 2; i >= 1; i--) {
        rightMaxArr[i] = tempRightMax;
        if (height[i] > tempRightMax) tempRightMax = height[i];
    }

    // *记录当前元素左边的最大值
    int leftMax = height[0], rightMax;

    for (int i = 1; i < heightSize - 1; ++i) {
        // 找左右两边最高的列
        rightMax = rightMaxArr[i];
        int min = leftMax < rightMax ? leftMax : rightMax;
        // 只有左边最高的和右边最高的,二者中的较小者比当年的列高,当前列才能接得住水
        // 较小者小于等于当前列,接不住水
        if (min > height[i]) res += min - height[i];
        // *更新leftMax
        if (height[i] > leftMax) leftMax = height[i];
    }
    return res;
}
// 双指针优化掉leftMaxArr、rightMaxArr
int trap(int *height, int heightSize) {
    int res = 0;

    // 柱子能接到的水 = min{左右两边最高的柱子} - 当前柱子的高度
    // L、R是两根柱子,本应该有L的两个变量leftMaxOfL、rightMaxOfL和R的两个变量leftMaxOfR、rightMaxOfR
    // 由于L < R,所以leftMaxOfL <= leftMaxOfR,rightMaxOfR <= rightMaxOfL
    // 从而如果leftMaxOfL > rightMaxOfR,则一定有leftMaxOfR >= rightMaxOfR,即在R处,左边最高的柱子大于右边最高的柱子,此时可以计算在R处能接到的水
    // 如果leftMaxOfL < rightMaxOfR,则一定有rightMaxOfL >= leftMaxOfL,即在L处,右边最高的柱子大于左边最高的柱子,此时可以计算在L处能接到的水
    // 实际上只用到了leftMaxOfL和rightMaxOfR这两个变量

    // 记录当前元素左边的最大值
    int leftMaxOfL = height[0], rightMaxOfR = height[heightSize - 1];
    // 双指针,L、R代替原来的i
    int L = 1, R = heightSize - 2;

    for (int i = 1; i < heightSize - 1; ++i) {
        // leftMaxOfL = leftMaxArr[L] = max(leftMaxArr[L-1], height[L-1])
        // 即当前位置的左侧最大元素,是上个元素的左侧最大元素和上个元素两者中的较大者,height[L-1]是可能成为leftMax的变量
        // 同理,当前位置的右侧最大元素,是后个元素的右侧最大元素和后个元素两者中的较大者,height[R+1]是可能成为rightMax的变量
        if (height[L - 1] < height[R + 1]) {
            // 从左往右更新
            // 当height[L-1] < height[R+1]时,一定有leftMaxOfL < rightMaxOfR todo ???
            // [1,5,3,4,2,6] 
            // 此时min=leftMaxOfL
            int min = leftMaxOfL;
            if (min > height[L]) res += min - height[L];
            // 更新leftMax
            if (height[L] > leftMaxOfL) leftMaxOfL = height[L];
            L++;
        } else {
            // 从右往左更新
            int min = rightMaxOfR;
            if (min > height[R]) res += min - height[R];
            // 更新rightMax
            if (height[R] > rightMaxOfR) rightMaxOfR = height[R];
            R--;
        }
    }
    return res;
}
// todo 栈
int trap(int *height, int heightSize) {
    int res = 0;
    int stack[heightSize];
    int top = 0;
    int current = 0;
    while (current < heightSize) {
        // 当前高度大于栈顶高度,说明之前的地方能接水,持续出栈到栈顶高度大于等于当前高度或者直到栈空为止
        while (top > 0 && height[current] > height[stack[top - 1]]) {
            // 栈顶出栈
            int h = height[stack[top - 1]];
            top--;
            if (top == 0) break;
            // 两个柱子间的距离
            int distance = current - stack[top - 1] - 1;
            // 新的栈顶高度和当前高度的较小者
            int min = height[stack[top - 1]] < height[current] ? height[stack[top - 1]] : height[current];
            res += distance * (min - h);
        }
        stack[top++] = current;
        current++;
    }
    return res;
}

滑动窗口

3. 无重复字符的最长子串

int lengthOfLongestSubstring(char *s) {
    int len = strlen(s);
    if (len < 2) return len;

    // 记录当前在滑动窗口中的字符
    int *hashSet = (int *) calloc(128, sizeof(int));
    int left = 0, right = 0;
    hashSet[s[right]] = 1;
    int res = 1;

    while (right + 1 < len) {
        // 待进入窗口的下个字符
        char nextChar = s[right + 1];
        if (hashSet[nextChar] == 0) {
            // 未出现过就加入到窗口中
            hashSet[nextChar] = 1;
            right++;
            // 更新最大长度
            if ((right - left + 1) > res) res = right - left + 1;
        } else {
            // 已经出现在窗口中,就移动left
            while (left <= right && s[left] != nextChar) {
                // 去掉left之前的字符
                hashSet[s[left]] = 0;
                left++;
            }
            // 此时s[left]和nextChar相等
            // 窗口整体右移一格
            left++;
            right++;
        }
    }
    return res;
}

438. 找到字符串中所有字母异位词

int *findAnagrams(char *s, char *p, int *returnSize) {
    *returnSize = 0;
    int lenS = strlen(s);
    int lenP = strlen(p);
    if (lenS < lenP) return NULL;

    int *res = (int *) malloc(sizeof(int) * (lenS - lenP + 1));
    int *count = (int *) calloc(26, sizeof(int));
    // 记录count数组中不为0的个数
    int differ = 0;
    for (int i = 0; i < lenP; ++i) {
        // 累加p中对应字符出现的次数
        count[p[i] - 'a']++;
        // 减去s中已经出现的字符的次数
        count[s[i] - 'a']--;
    }

    // 记录不为0的个数
    for (int i = 0; i < 26; ++i)
        if (count[i] != 0) differ++;
    // 若刚好抵消掉p中各个字符出现的次数,说明是异位词
    if (differ == 0) res[(*returnSize)++] = 0;

    // 窗口左右两端
    int left = 1, right = lenP;
    // 开始滑动窗口,窗口大小和p的长度一致
    while (right < lenS) {
        // left-1移除窗口
        // 移动之前就是0,说明移动后不为0的字符会多一个
        if (count[s[left - 1] - 'a'] == 0) differ++;
        count[s[left - 1] - 'a']++;
        // 移动之后是0,说明移动后不为0的字符会少一个
        if (count[s[left - 1] - 'a'] == 0) differ--;

        // right移入窗口
        if (count[s[right] - 'a'] == 0) differ++;
        count[s[right] - 'a']--;
        if (count[s[right] - 'a'] == 0) differ--;

        if (differ == 0) res[(*returnSize)++] = left;
        left++;
        right++;
    }

    return res;
}

子串

560. 和为 K 的子数组

// 暴力枚举(超时)
int subarraySum(int *nums, int numsSize, int k) {
    int res = 0;
    for (int i = 0; i < numsSize; ++i) {
        int tempSum = 0;
        for (int j = i; j < numsSize; ++j) {
            tempSum += nums[j];
            if (tempSum == k) res++;
        }
    }
    return res;
}
// 记录前缀和(超时)
int subarraySum(int *nums, int numsSize, int k) {
    int *prefixSum = (int *) calloc(numsSize + 1, sizeof(int));
    for (int i = 1; i <= numsSize; ++i) {
        prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
    }

    int res = 0;
    for (int i = 0; i < numsSize; ++i) {
        for (int j = i; j < numsSize; ++j) {
            if (prefixSum[j + 1] - prefixSum[i] == k) res++;
        }
    }
    return res;
}
// todo
// 只关心有多少个prefixSum[j]满足prefixSum[j]等于prefixSum[i] - k,不关心j的具体位置
int subarraySum(int *nums, int numsSize, int k) {
    int res = 0;
    const int size = 20000 * 1000;
    // 最多20000个数,和的范围是[-20000000, 20000000],右移20000000,使其下标从0开始,[0, 40000000]
    int *hashMap = (int *) calloc(size * 2, sizeof(int));

    // 记录前缀和(prefixSum[i]不包括i处的元素)
    // j < i, prefixSum[i] - prefixSum[j] = k时,说明[j, i-1]就是一个和为k的子数组
    // 转化为累加prefixSum[j]等于prefixSum[i] - k的个数
    int *prefixSum = (int *) calloc(numsSize + 1, sizeof(int));
    // 第一个元素的前缀和是0,单独记录下这个0出现过一次(不然就修改定义,prefixSum[i]改成包括i处的元素)
    hashMap[0 + size]++;
    for (int i = 1; i <= numsSize; ++i) {
        prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
        res += hashMap[prefixSum[i] - k + size];
        // 累加前缀和为prefixSum[i]出现的次数
        hashMap[prefixSum[i] + size]++;
    }

    return res;
}

239. 滑动窗口最大值


76. 最小覆盖子串

普通数组

53. 最大子数组和

int maxSubArray(int *nums, int numsSize) {
    int res = nums[0];

    // dp[i]表示到i位置的最大连续子数组之和的最大值(错,有后效性,因为不确定i在子数组中是第几个元素)
    // dp[i]表示以i位置结尾的最大连续子数组之和的最大值(对,无后效性)
    int dp[numsSize];
    dp[0] = nums[0];
    for (int i = 1; i < numsSize; ++i) {
        // 必须以nums[i]结尾
        if (dp[i - 1] > 0)
            dp[i] = dp[i - 1] + nums[i];
        else
            dp[i] = nums[i];
        if (dp[i] > res) res = dp[i];
    }
    return res;
}
// dp[i]只和dp[i-1]相关,可以使用滚动变量优化
int maxSubArray(int *nums, int numsSize) {
    int res = nums[0];

    // pre表示以i位置结尾的最大连续子数组之和的最大值
    int pre = nums[0];
    for (int i = 1; i < numsSize; ++i) {
        // 必须以nums[i]结尾
        if (pre > 0)
            pre = pre + nums[i];
        else
            pre = nums[i];
        if (pre > res) res = pre;
    }
    return res;
}
int max(int a, int b, int c) {
    int d = a > b ? a : b;
    return d > c ? d : c;
}

// 必须经过mid和mid+1
int maxCrossingSum(int *nums, int left, int mid, int right) {
    int leftMax = nums[mid];
    int rightMax = nums[mid + 1];

    int index = mid;
    int tempMax = 0;
    // 找左边以mid结尾的最大连续子数组的和
    while (index >= left) {
        tempMax += nums[index];
        if (tempMax > leftMax) leftMax = tempMax;
        index--;
    }

    index = mid + 1;
    tempMax = 0;
    // 找右边以mid+1开头的最大连续子数组的和
    while (index <= right) {
        tempMax += nums[index];
        if (tempMax > rightMax) rightMax = tempMax;
        index++;
    }

    return leftMax + rightMax;
}

int maxSubArraySum(int *nums, int left, int right) {
    if (left == right) return nums[left];
    // 中偏左
    int mid = left + ((right - left) >> 1);

    // 分三类,包含所有情况
    // 第一类:以mid结尾的
    // 第二类:以mid+1开头的
    // 第三类:经过mid和mid+1的
    return max(maxSubArraySum(nums, left, mid),
               maxSubArraySum(nums, mid + 1, right),
               maxCrossingSum(nums, left, mid, right));
}

// 分治
int maxSubArray(int *nums, int numsSize) {
    if (numsSize == 0) return 0;
    return maxSubArraySum(nums, 0, numsSize - 1);
}

56. 合并区间

int cmp(const int **a, const int **b) {
    return *a[0] > *b[0];
}

int **merge(int **intervals, int intervalsSize, int *intervalsColSize, int *returnSize, int **returnColumnSizes) {
    *returnSize = 0;
    int **res = (int **) malloc(sizeof(int *) * intervalsSize);
    *returnColumnSizes = (int *) malloc(sizeof(int) * intervalsSize);
    for (int i = 0; i < intervalsSize; ++i) {
        res[i] = (int *) malloc(sizeof(int) * 2);
        (*returnColumnSizes)[i] = 2;
    }

    // 按第一列排序
    qsort(intervals, intervalsSize, sizeof(int) * 2, cmp);

    for (int i = 0; i < intervalsSize; ++i) {
        int left = intervals[i][0];
        int right = intervals[i][1];
        // 合并
        while ((i < intervalsSize - 1) && right >= intervals[i + 1][0]) {
            if (intervals[i + 1][1] > right) right = intervals[i + 1][1];
            i++;
        }
        res[*returnSize][0] = left;
        res[*returnSize][1] = right;
        (*returnSize)++;
    }
    return res;
}

189. 轮转数组

void reverse(int *nums, int left, int right) {
    int temp;
    while (left < right) {
        temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
        left++;
        right--;
    }
}

// 三次原地反转
void rotate(int *nums, int numsSize, int k) {
    k %= numsSize;
    reverse(nums, 0, numsSize - 1);
    reverse(nums, 0, k - 1);
    reverse(nums, k, numsSize - 1);
}

238. 除自身以外数组的乘积

int *productExceptSelf(int *nums, int numsSize, int *returnSize) {
    *returnSize = numsSize;
    int *res = (int *) malloc(sizeof(int) * numsSize);

    // 记录左右乘积
    int leftMulArray[numsSize], rightMulArray[numsSize];
    leftMulArray[0] = 1;
    for (int i = 1; i < numsSize; ++i)
        leftMulArray[i] = leftMulArray[i - 1] * nums[i - 1];
    rightMulArray[numsSize - 1] = 1;
    for (int i = numsSize - 2; i >= 0; i--)
        rightMulArray[i] = rightMulArray[i + 1] * nums[i + 1];

    // 左右乘积相乘
    for (int i = 0; i < numsSize; ++i)
        res[i] = leftMulArray[i] * rightMulArray[i];

    return res;
}
// 空间复杂度O(1)
int *productExceptSelf(int *nums, int numsSize, int *returnSize) {
    *returnSize = numsSize;
    int *res = (int *) malloc(sizeof(int) * numsSize);

    res[0] = 1;
    // 左边乘积存入res
    for (int i = 1; i < numsSize; ++i)
        res[i] = res[i - 1] * nums[i - 1];

    // 右边乘积存入rightMul
    int rightMul = 1;
    for (int i = numsSize - 2; i >= 0; i--) {
        rightMul = rightMul * nums[i + 1];
        // 左右乘积相乘
        res[i] = res[i] * rightMul;
    }
    return res;
}

41. 缺失的第一个正数

class Solution {
public:
    // 原地映射:把值为 value 的元素映射到数组中下标为 value-1 的位置
    int firstMissingPositive(vector<int> &nums) {
        int length = nums.size();
        // 待映射的值
        int value;
        // 临时保存被 value 重新映射后覆盖掉的值,之后再判断 temp 是否也需要重新映射
        int temp;

        for (int i = 0; i < length; ++i) {
            // 跳过映射不到数组中的元素
            if (nums[i] <= 0 || nums[i] > length) continue;
            // 跳过已经映射好的元素
            if (nums[i] == i + 1) continue;

            // 准备重新映射nums[i]
            value = nums[i];
            // 把原来的位置设置-1,表示这个位置已经处理过了
            nums[i] = -1;
            // 保存可能需要映射的 nums[value - 1]
            temp = nums[value - 1];
            // 把值为 value 的元素映射到数组中下标为 value-1 的位置
            nums[value - 1] = value;

            // 如果 temp 也能映射到数组中,并且尚未映射过
            while (temp > 0 && temp <= length && nums[temp - 1] != temp) {
                value = temp;
                temp = nums[value - 1];
                nums[value - 1] = value;
            }
        }

        // 遍历寻找第一个空缺
        for (int j = 0; j < length; ++j)
            if (nums[j] != j + 1)
                return j + 1;

        // 重新映射后,数组中没有空缺(没有值-1),说明缺失的第一个正数就是 length + 1
        return length + 1;
    }
};
class Solution {
public:
    // 原地映射:把值为 value 的元素映射到数组中下标为 value-1 的位置
    // 优化了对映射后覆盖掉的值的处理
    int firstMissingPositive(vector<int> &nums) {
        int length = nums.size();
        for (int i = 0; i < length; ++i) {
            // 如果 nums[i] 也能映射到数组中,并且尚未映射过
            while (nums[i] > 0 && nums[i] <= length && nums[nums[i] - 1] != nums[i]) {
                // 把 nums[i] 映射到 nums[nums[i] - 1],nums[nums[i] - 1] 放在 nums[i] 
                // 然后继续判断新的 nums[i] 是否也需要映射
                swap(nums[nums[i] - 1], nums[i]);
            }
        }
        // 遍历寻找第一个空缺
        for (int i = 0; i < length; ++i)
            if (nums[i] != i + 1)
                return i + 1;
        // 重新映射后,数组中没有空缺(没有值-1),说明缺失的第一个正数就是 length + 1
        return length + 1;
    }
};
class Solution {
public:
    // 原地映射:把可以映射到的地方的元素改成负数
    int firstMissingPositive(vector<int> &nums) {
        int length = nums.size();
        // 非正数改成 length + 1
        for (int &value: nums)
            if (value <= 0) value = length + 1;
        for (int i = 0; i < length; ++i) {
            // 待映射的值
            int value = abs(nums[i]);
            // 如果可以映射到数组里,就把映射到的地方的元素改成负数
            // 用负数标记已经被映射过,而不是通过 nums[value-1] == value 来判断
            if (value <= length) nums[value - 1] = -abs(nums[value - 1]);
        }
        for (int i = 0; i < length; ++i)
            if (nums[i] > 0) return i + 1;
        // 重新映射后,数组中没有空缺(没有值大于0),说明缺失的第一个正数就是 length + 1
        return length + 1;
    }
};

矩阵

73. 矩阵置零

void setZeroes(int **matrix, int matrixSize, int *matrixColSize) {
    int *hashMapRow = (int *) calloc(matrixSize, sizeof(int));
    int *hashMapColumn = (int *) calloc(*matrixColSize, sizeof(int));
    for (int i = 0; i < matrixSize; ++i) {
        for (int j = 0; j < *matrixColSize; ++j) {
            if (matrix[i][j] == 0) {
                // 记录下i行j列,等会置零
                hashMapRow[i] = 1;
                hashMapColumn[j] = 1;
            }
        }
    }

    for (int i = 0; i < matrixSize; ++i)
        for (int j = 0; j < *matrixColSize; ++j)
            if (hashMapRow[i] == 1 || hashMapColumn[j] == 1) matrix[i][j] = 0;
}
// 空间复杂度O(1)
void setZeroes(int **matrix, int matrixSize, int *matrixColSize) {
    bool flagRow = false;
    bool flagColumn = false;
    // 判断首行首列是否要同时置零
    bool flag = matrix[0][0] == 0;
    for (int i = 0; i < matrixSize; ++i) {
        for (int j = 0; j < *matrixColSize; ++j) {
            if (matrix[i][j] == 0) {
                if (i == 0) {
                    // 第一行要置零
                    flagRow = true;
                    continue;
                }
                if (j == 0) {
                    // 第一列要置零
                    flagColumn = true;
                    continue;
                }
                // 用原矩阵的第一行第一列标记是否要置零
                matrix[0][j] = 0;
                matrix[i][0] = 0;
            }
        }
    }

    // 先处理除了第一行第一列其他的位置
    for (int i = 1; i < matrixSize; ++i)
        for (int j = 1; j < *matrixColSize; ++j)
            if (matrix[0][j] == 0 || matrix[i][0] == 0) matrix[i][j] = 0;

    // 处理第一行第一列
    if (flag || flagRow)
        for (int i = 0; i < *matrixColSize; ++i) matrix[0][i] = 0;
    if (flag || flagColumn)
        for (int i = 0; i < matrixSize; ++i) matrix[i][0] = 0;
}

54. 螺旋矩阵

int *spiralOrder(int **matrix, int matrixSize, int *matrixColSize, int *returnSize) {
    *returnSize = matrixSize * (*matrixColSize);
    int *res = (int *) malloc(sizeof(int) * (*returnSize));
    int index = 0;
    // 上下边界
    int up = 0, down = matrixSize - 1;
    // 左右边界
    int left = 0, right = *matrixColSize - 1;
    
    while (true) {
        // 向右处理up行
        for (int i = left; i <= right; ++i)
            res[index++] = matrix[up][i];
        if (++up > down) break;

        // 向下处理right列
        for (int i = up; i <= down; ++i)
            res[index++] = matrix[i][right];
        if (--right < left)break;

        // 向左处理down行
        for (int i = right; i >= left; i--)
            res[index++] = matrix[down][i];
        if (--down < up)break;

        // 向上处理left列
        for (int i = down; i >= up; i--)
            res[index++] = matrix[i][left];
        if (++left > right) break;
    }

    return res;
}

48. 旋转图像

void rotate(int **matrix, int matrixSize, int *matrixColSize) {
    int temp[matrixSize][matrixSize];
    // 放到新矩阵中
    for (int i = 0; i < matrixSize; ++i) {
        for (int j = 0; j < matrixSize; ++j) {
            temp[j][matrixSize - 1 - i] = matrix[i][j];
        }
    }

    // 放回原矩阵
    for (int i = 0; i < matrixSize; ++i)
        for (int j = 0; j < matrixSize; ++j)
            matrix[i][j] = temp[i][j];
}
// todo 原地旋转
void rotate(int **matrix, int matrixSize, int *matrixColSize) {
    for (int i = 0; i < matrixSize / 2; ++i) {
        for (int j = 0; j < (matrixSize + 1) / 2; ++j) {
            int temp = matrix[i][j];
            matrix[i][j] = matrix[matrixSize - j - 1][i];
            matrix[matrixSize - j - 1][i] = matrix[matrixSize - i - 1][matrixSize - j - 1];
            matrix[matrixSize - i - 1][matrixSize - j - 1] = matrix[j][matrixSize - i - 1];
            matrix[j][matrixSize - i - 1] = temp;
        }
    }
}
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

void rotate(int **matrix, int matrixSize, int *matrixColSize) {
    // 水平翻转
    for (int i = 0; i < matrixSize / 2; ++i)
        for (int j = 0; j < matrixSize; ++j)
            swap(&matrix[i][j], &matrix[matrixSize - 1 - i][j]);

    // 主对角线翻转
    for (int i = 0; i < matrixSize; ++i)
        for (int j = i + 1; j < matrixSize; ++j)
            swap(&matrix[i][j], &matrix[j][i]);
}

240. 搜索二维矩阵 II

bool binarySearch(int *array, int left, int right, int target) {
    int mid;
    while (left <= right) {
        mid = left + ((right - left) >> 1);
        if (array[mid] == target) {
            return true;
        } else if (array[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return false;
}

// 一行一行的二分查找
bool searchMatrix(int **matrix, int matrixSize, int *matrixColSize, int target) {
    for (int i = 0; i < matrixSize; ++i)
        if (binarySearch(matrix[i], 0, *matrixColSize - 1, target)) return true;
    return false;
}
// 从右上角往左或者往下遍历
bool searchMatrix(int **matrix, int matrixSize, int *matrixColSize, int target) {
    int row = 0, column = *matrixColSize - 1;
    while (row < matrixSize && column >= 0) {
        int cur = matrix[row][column];
        if (cur == target)
            return true;
        else if (cur > target)
            // 往左
            column--;
        else
            // 往下
            row++;
    }
    return false;
}
// todo 有bug
void display(int **matrix, int matrixSize, int *matrixColSize) {
    for (int i = 0; i < matrixSize; ++i) {
        for (int j = 0; j < *matrixColSize; ++j) {
            printf("%d ", matrix[i][j]);
        }
        puts("");
    }
    puts("");
}

bool search(int **matrix, int left, int right, int up, int down, int target) {
    if (left > right || up > down) return false;
    if (left == right && up == down) return matrix[left][up] == target;
    if (left == right - 1 && up == down - 1) {
        return matrix[left][up] == target
               || matrix[left + 1][up] == target
               || matrix[left][up + 1] == target
               || matrix[left + 1][up + 1] == target;
    }

    int midRow = up + ((down - up) >> 1);
    int midColumn = left + ((right - left) >> 1);
    int cur = matrix[midRow][midColumn];
    printf("left=%d right=%d up=%d down=%d midRow=%d midColumn=%d cur=%d\n", left, right, up, down, midRow, midColumn,
           cur);

    if (cur == target) return true;
    if (cur > target) {
        // 丢弃右下角(右下角全都大于target)
        puts("左上角");
        bool a = search(matrix, left, midColumn, up, midRow, target);  // 左上角
        puts("右上角");
        bool b = search(matrix, midColumn + 1, right, up, midRow - 1, target); // 右上角
        puts("左下角");
        bool c = search(matrix, left, midColumn - 1, midRow + 1, down, target);    // 左下角
        return a || b || c;
//        return search(matrix, left, midColumn, up, midRow, target)  // 左上角
//               || search(matrix, midColumn + 1, right, up, midRow - 1, target)  // 右上角
//               || search(matrix, left, midColumn - 1, midRow + 1, down, target);    // 左下角
    } else {
        // 丢弃左上角(左上角全都小于target)
        puts("右下角");
        bool a = search(matrix, midColumn, right, midRow, down, target); // 右下角
        puts("右上角");
        bool b = search(matrix, midColumn + 1, right, up, midRow - 1, target);  // 右上角
        puts("左下角");
        bool c = search(matrix, left, midColumn - 1, midRow + 1, down, target);    // 左下角
        return a || b || c;
//        return search(matrix, midColumn, right, midRow, down, target)   // 右下角
//               || search(matrix, midColumn + 1, right, up, midRow - 1, target)  // 右上角
//               || search(matrix, left, midColumn - 1, midRow + 1, down, target);    // 左下角
    }
}

bool searchMatrix(int **matrix, int matrixSize, int *matrixColSize, int target) {
    display(matrix, matrixSize, matrixColSize);
    return search(matrix, 0, *matrixColSize - 1, 0, matrixSize - 1, target);
}

链表

160. 相交链表

// 返回两个单链表相交的起始节点
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    int len1 = 0, len2 = 0;
    struct ListNode *p = headA, *q = headB;
    // 求长度
    while (p != NULL) {
        len1++;
        p = p->next;
    }
    while (q != NULL) {
        len2++;
        q = q->next;
    }

    // 长链表先走
    p = headA;
    q = headB;
    if (len1 > len2)
        for (int i = 0; i < len1 - len2; ++i)
            p = p->next;
    else
        for (int i = 0; i < len2 - len1; ++i)
            q = q->next;
    // p、q距离尾节点距离相同时,同时出发    
    while (p != NULL) {
        if (p == q)return p;
        p = p->next;
        q = q->next;
    }
    return NULL;
}

206. 反转链表

// 迭代
struct ListNode *reverseList(struct ListNode *head) {
    struct ListNode *pre = NULL;
    struct ListNode *next;
    struct ListNode *cur = head;

    // 原地反转
    while (cur != NULL) {
        next = cur->next;
        cur->next = pre;
        pre = cur;
        cur = next;
    }
    return pre;
}
// 递归
struct ListNode *reverseList(struct ListNode *head) {
    // 递归出口
    if (head == NULL || head->next == NULL) return head;
    // 递归式
    struct ListNode *newHead = reverseList(head->next); // 递归反转后面的链表
    head->next->next = head; // 下个结点也就是反转后的尾节点,指向自己
    head->next = NULL; // 自己作为新的尾节点
    return newHead;
}

234. 回文链表

// 返回向上取整的中间节点
struct ListNode *findMid(struct ListNode *head) {
    struct ListNode *slow = head;
    struct ListNode *fast = head;
    while (fast != NULL && fast->next != NULL) {
        fast = fast->next->next;
        slow = slow->next;
    }
    return slow;
}

// 原地反转
struct ListNode *reverseList(struct ListNode *head) {
    struct ListNode *pre = NULL;
    struct ListNode *cur = head;
    struct ListNode *next;
    while (cur != NULL) {
        next = cur->next;
        cur->next = pre;
        pre = cur;
        cur = next;
    }
    return pre;
}


bool isPalindrome(struct ListNode *head) {
    struct ListNode *mid = findMid(head);
    mid = reverseList(mid);

    struct ListNode *p = head;
    struct ListNode *q = mid;
    while (q != NULL) {
        if (p->val != q->val) return false;
        p = p->next;
        q = q->next;
    }
    return true;
}

141. 环形链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    if (head == NULL || head->next == NULL) return false;

    struct ListNode *slow = head;
    struct ListNode *fast = head->next;
    while (fast != NULL && fast->next != NULL) {
        if (slow == fast)return true;
        slow = slow->next;
        fast = fast->next->next;
    }
    return false;
}

142. 环形链表 II

// todo
struct ListNode *detectCycle(struct ListNode *head) {
    // 环外节点数a 环内节点数b
    // 快慢指针经过的节点个数关系: f = 2s
    // 相遇时: f = s + n*b -> s = n*b, f = 2*n*b
    // 走到入口节点经过的节点个数k = a + n*b, 先前进a步到入口节点, 然后在环里转圈
    // f = 0, s = n*b -> f = a, s = a + n*b相遇在入口节点

    struct ListNode *slow = head, *fast = head;
    while (fast != NULL && fast->next != NULL) {
        slow = slow->next;
        fast = fast->next->next;
        // 首次相遇时,slow已经跑了b步,只需跑a步就能到达入口
        // fast返回开头head节点,也只需跑a步就能到达入口
        // 此时a是几并不知道,但是可以确定的是,slow和fast都在跑a步就会在入口相遇
        if (slow == fast) {
            fast = head;
            // 此时f = 0, s = 1*b
            while (slow != fast) {
                slow = slow->next;
                fast = fast->next;
            }
            // 结束时f = a, s = a + 1*b
            return slow;
        }
    }

    return NULL;
}

21. 合并两个有序链表

// 递归
struct ListNode *mergeTwoLists(struct ListNode *list1, struct ListNode *list2) {
    // 递归出口
    if (list1 == NULL) return list2;
    if (list2 == NULL) return list1;
    // 递归体
    if (list1->val < list2->val) {
        list1->next = mergeTwoLists(list1->next, list2);
        return list1;
    } else {
        list2->next = mergeTwoLists(list1, list2->next);
        return list2;
    }
}

2. 两数相加

// 先反转两个链表,再从低位往高位计算,记录进位
struct ListNode *addTwoNumbers(struct ListNode *l1, struct ListNode *l2) {
    struct ListNode *n1 = (l1);
    struct ListNode *n2 = (l2);
    struct ListNode *dummyHead = (struct ListNode *) malloc(sizeof(struct ListNode));
    dummyHead->next = NULL;
    struct ListNode *pre = dummyHead;

    // 进位
    int carry = 0;
    while (n1 != NULL && n2 != NULL) {
        struct ListNode *node = (struct ListNode *) malloc(sizeof(struct ListNode));
        node->next = NULL;
        // 计算
        node->val = (n1->val + n2->val + carry) % 10;
        carry = (n1->val + n2->val + carry) / 10;
        // 拼接
        pre->next = node;
        pre = pre->next;
        n1 = n1->next;
        n2 = n2->next;
    }
    while (n1 != NULL) {
        struct ListNode *node = (struct ListNode *) malloc(sizeof(struct ListNode));
        node->next = NULL;
        node->val = (n1->val + carry) % 10;
        carry = (n1->val + carry) / 10;
        pre->next = node;
        pre = pre->next;
        n1 = n1->next;
    }
    while (n2 != NULL) {
        struct ListNode *node = (struct ListNode *) malloc(sizeof(struct ListNode));
        node->next = NULL;
        node->val = (n2->val + carry) % 10;
        carry = (n2->val + carry) / 10;
        pre->next = node;
        pre = pre->next;
        n2 = n2->next;
    }
    // 进位可能导致最后多出一位
    if (carry == 1) {
        struct ListNode *node = (struct ListNode *) malloc(sizeof(struct ListNode));
        node->next = NULL;
        node->val = 1;
        pre->next = node;
    }
    return (dummyHead->next);
}

19. 删除链表的倒数第 N 个结点

struct ListNode *removeNthFromEnd(struct ListNode *head, int n) {
    struct ListNode *dummyHead = (struct ListNode *) malloc(sizeof(struct ListNode));
    dummyHead->next = head;
    struct ListNode *slow = dummyHead, *fast = dummyHead;

    int count = n + 1;
    while (count-- > 0) fast = fast->next;
    while (fast != NULL) {
        slow = slow->next;
        fast = fast->next;
    }

    slow->next = slow->next->next;
    return dummyHead->next;
}

24. 两两交换链表中的节点

struct ListNode *swapPairs(struct ListNode *head) {
    if (head == NULL || head->next == NULL) return head;
    struct ListNode *nextNode = head->next->next;
    struct ListNode *right = head->next;
    right->next = head;
    head->next = swapPairs(nextNode);
    return right;
}

25. K 个一组翻转链表

class Solution {
public:

    ListNode *reverseList(ListNode *head, ListNode *tail) {
        ListNode *pre = tail->next, *cur = head, *next;
        ListNode *nextNode = tail->next;
        while (cur != nullptr && cur != nextNode) {
            next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }

        return pre;
    }

    ListNode *reverseKGroup(ListNode *head, int k) {
        // 虚拟头节点,接在链表最前面,使得第一段要反转的链表部分的处理和后面统一
        ListNode *dummyHead = new ListNode(0, head);
        ListNode *pre = dummyHead;
        ListNode *left = dummyHead;
        ListNode *right;
        // 后移次数
        int count;

        while (pre->next != nullptr) {
            // left、right 移到下一段要反转的链表的头部
            left = pre->next;
            right = left;
            // right 后移 k-1 个节点
            for (count = k - 1; count != 0 && right->next != nullptr; count--) {
                right = right->next;
            }
            // 链表元素总数小于 k
            if (count != 0) return dummyHead->next;
            // 把反转后的部分接到前面的链表上
            pre->next = reverseList(left, right);
            // 反转后 left 节点变成反转部分的尾节点,也就是下一段要反转的部分的上一个节点
            pre = left;
        }

        return dummyHead->next;
    }
};

138. 随机链表的复制

struct Node *copyRandomList(struct Node *head) {
    if (head == NULL) return NULL;
    struct Node *pre = head;

    // 遍历原链表,在每个节点后面插入新节点
    while (pre != NULL) {
        struct Node *node = (struct Node *) malloc(sizeof(struct Node));
        node->val = pre->val;
        // 接在原节点的后面
        node->next = pre->next;
        pre->next = node;
        pre = pre->next->next;
    }

    pre = head;
    while (pre != NULL) {
        // 修改random指针
        if (pre->random != NULL)
            pre->next->random = pre->random->next;
        else
            pre->next->random = NULL;
        pre = pre->next->next;
    }

    pre = head;
    struct Node *res = head->next, *cur = head->next;
    while (cur != NULL && cur->next != NULL) {
        // 改回原链表节点的next指针
        pre->next = pre->next->next;
        pre = pre->next;
        // 新链表的节点从原链表中分离出来,串在一起
        cur->next = cur->next->next;
        cur = cur->next;
    }
    // 原链表尾节点的next指针
    pre->next = NULL;
    return res;
}

148. 排序链表

// 归并
struct ListNode *merge(struct ListNode *l1, struct ListNode *l2) {
    if (l1 == NULL || l2 == NULL) return l1 == NULL ? l2 : l1;
    struct ListNode *dummyHead = (struct ListNode *) malloc(sizeof(struct ListNode));
    struct ListNode *pre = dummyHead;
    while (l1 != NULL && l2 != NULL) {
        if (l1->val < l2->val) {
            pre->next = l1;
            l1 = l1->next;
        } else {
            pre->next = l2;
            l2 = l2->next;
        }
        pre = pre->next;
    }

    if (l1 != NULL) pre->next = l1;
    if (l2 != NULL) pre->next = l2;
    return dummyHead->next;
}

struct ListNode *sortList(struct ListNode *head) {
    // 统计链表长
    int len = 0;
    struct ListNode *temp = head;
    while (temp != NULL) {
        len++;
        temp = temp->next;
    }

    struct ListNode *dummyHead = (struct ListNode *) malloc(sizeof(struct ListNode));
    dummyHead->next = head;

    // 步长每次乘二
    for (int gap = 1; gap < len; gap <<= 1) {
        struct ListNode *pre = dummyHead;
        struct ListNode *cur = dummyHead->next;

        // 每次从一组元素的首个元素节点开始(两个子链表为一组)
        while (cur != NULL) {
            // 长度为gap的子链表l1
            struct ListNode *l1 = cur;
            int i = 1;
            while (i < gap && cur->next != NULL) {
                cur = cur->next;
                i++;
            }

            // 子链表l2
            struct ListNode *l2 = cur->next;
            // 把l2从l1后面断开
            cur->next = NULL;

            // 找到子链表l2的末尾,l2可能是最后一个子链表并且长度小于等于gap
            // l2后面可能还有
            cur = l2;
            i = 1;
            while (i < gap && cur != NULL && cur->next != NULL) {
                cur = cur->next;
                i++;
            }


            struct ListNode *next = NULL;
            // l2后面还有节点时
            if (cur != NULL) {
                // 下一组的起点(两个子链表为一组)
                next = cur->next;
                // 断开,l2变成完成的一条链表
                cur->next = NULL;
            }

            // 把这组的两个子链表合并
            pre->next = merge(l1, l2);
            // pre移到合并后的最后一个节点,等待接上下一组合并后的首个节点
            while (pre->next != NULL) {
                pre = pre->next;
            }
            // 进入下一组的归并
            cur = next;
        }
    }

    return dummyHead->next;
}

23. 合并 K 个升序链表

class Solution {
public:
    // 通过遍历所有链表的头节点,找到每次要合并的节点
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        // 没有元素直接返回
        if (lists.empty()) return nullptr;
        ListNode *dummyHead = new ListNode(0, nullptr);
        ListNode *pre = dummyHead;
        int length = lists.size();

        while (true) {
            // 记录当前所有链表中头节点值最小的链表在 lists 中的下标
            int minHeadIndex = -1;
            for (int i = 0; i < length; ++i) {
                // 链表非空的情况下,minHeadIndex 为 -1 或者 lists[i] 节点值更小,则更新 minHeadIndex
                if ((lists[i] != nullptr) &&
                    (minHeadIndex == -1 || (lists[i]->val < lists[minHeadIndex]->val)))
                    minHeadIndex = i;
            }

            // 已经找不到元素了(链表全都是空的),则函数返回
            if (minHeadIndex == -1) return dummyHead->next;
            // 否则,移出最小元素,并且合并到最终结果中
            ListNode *node = lists[minHeadIndex];
            lists[minHeadIndex] = lists[minHeadIndex]->next;
            node->next = nullptr;
            pre->next = node;
            pre = pre->next;

            // 判断是否只剩下一条链表
            int finished = 0;
            for (auto &curHead: lists)
                if (curHead == nullptr) finished++;
            if (finished >= length - 1) break;
        }

        // 把最后剩下的一条链表直接接上
        for (auto &curHead: lists) {
            if (curHead != nullptr) {
                pre->next = curHead;
                return dummyHead->next;
            }
        }

        return dummyHead->next;
    }
};
class Solution {
public:
    // 合并两个链表
    ListNode *mergeTwoLists(ListNode *a, ListNode *b) {
        if ((!a) || (!b)) return a ? a : b;
        ListNode *dummyHead = new ListNode(0, nullptr);
        ListNode *pre = dummyHead;
        ListNode *p = a, *q = b;
        while (p && q) {
            if (p->val < q->val) {
                pre->next = p;
                p = p->next;
            } else {
                pre->next = q;
                q = q->next;
            }
            pre = pre->next;
        }
        pre->next = (p ? p : q);
        return dummyHead->next;
    }

    // 遍历链表数组,每次把一整条链表和最终结果合并
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        ListNode *res = nullptr;
        for (size_t i = 0; i < lists.size(); ++i)
            res = mergeTwoLists(res, lists[i]);
        return res;
    }
};
class Solution {
public:
    // 合并两个链表
    ListNode *mergeTwoLists(ListNode *a, ListNode *b) {
        if ((!a) || (!b)) return a ? a : b;
        ListNode *dummyHead = new ListNode(0, nullptr);
        ListNode *pre = dummyHead;
        ListNode *p = a, *q = b;
        while (p && q) {
            if (p->val < q->val) {
                pre->next = p;
                p = p->next;
            } else {
                pre->next = q;
                q = q->next;
            }
            pre = pre->next;
        }
        pre->next = (p ? p : q);
        return dummyHead->next;
    }

    ListNode *merge(vector<ListNode *> &lists, int left, int right) {
        if (left > right) return nullptr;
        if (left == right) return lists[left];
        int mid = (left + right) >> 1;
        return mergeTwoLists(merge(lists, left, mid), merge(lists, mid + 1, right));
    }

    // 分治合并
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        return merge(lists, 0, lists.size() - 1);
    }
};
class Solution {
public:
    // 自定义比较函数
    static bool cmp(ListNode *a, ListNode *b) {
        return (*a).val > (*b).val;
    }

    // 使用小顶堆,只有初始化堆的时候要遍历一次数组,之后每次把堆顶节点并入最终链表,并且把堆顶节点的后继加入堆中
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        // 没有元素直接返回
        if (lists.empty()) return nullptr;
        ListNode *dummyHead = new ListNode(0, nullptr);
        ListNode *pre = dummyHead;

        // 小顶堆
        priority_queue<ListNode *, vector<ListNode *>, decltype(&cmp)> heap(cmp);
        // 初始化小顶堆,把所有的链表头节点加入其中
        for (auto &curHead: lists) {
            if (curHead == nullptr) continue;
            heap.push(curHead);
            curHead = curHead->next;
        }

        while (true) {
            if (heap.empty() || heap.top() == nullptr) break;
            // 取出最小节点,并加入到最终的链表中
            ListNode *node = heap.top();
            heap.pop();
            pre->next = node;
            pre = pre->next;

            // 如果后面的节点非空的话,就加入到堆中
            if (node->next != nullptr) heap.push(node->next);
        }
        return dummyHead->next;
    }
};

146. LRU 缓存

// 用hashMap定位节点
class LRUCache {
    ListNode dummyHead;
    ListNode dummyTail;
    int capacity;
    Map<Integer, ListNode> map;

    public LRUCache(int capacity) {
        map = new HashMap<>();
        this.capacity = capacity;
        dummyHead = new ListNode();
        dummyTail = new ListNode();
        dummyHead.next = dummyTail;
        dummyTail.prev = dummyHead;
    }

    public int get(int key) {
        if (!map.containsKey(key)) return -1;
        ListNode node = map.get(key);
        moveToHead(node);
        return node.value;
    }

    public void addToHead(ListNode node) {
        map.put(node.key, node);
        node.next = dummyHead.next;
        dummyHead.next.prev = node;
        dummyHead.next = node;
        node.prev = dummyHead;
    }

    public void moveToHead(ListNode node) {
        // 断开
        node.prev.next = node.next;
        node.next.prev = node.prev;
        // 接上
        node.next = dummyHead.next;
        dummyHead.next.prev = node;
        node.prev = dummyHead;
        dummyHead.next = node;
    }

    public void deleteNode(ListNode node) {
        map.remove(node.key);
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    public void put(int key, int value) {
        if (map.containsKey(key)) {
            ListNode node = map.get(key);
            node.value = value;
            moveToHead(node);
            return;
        }

        if (map.size() == capacity) deleteNode(dummyTail.prev);

        ListNode node = new ListNode(key, value);
        addToHead(node);
    }
}

class ListNode {
    int key;
    int value;
    ListNode prev;
    ListNode next;

    public ListNode(int key, int value, ListNode prev, ListNode next) {
        this.key = key;
        this.value = value;
        this.prev = prev;
        this.next = next;
    }

    public ListNode(int key, int value) {
        this.key = key;
        this.value = value;
    }

    public ListNode() {
    }
}
posted @ 2024-01-09 14:52  n1ce2cv  阅读(6)  评论(0编辑  收藏  举报