数组_算法笔记

最大子数组和

链接:53. 最大子数组和        53. 最大子数组和 - 力扣(Leetcode)

题目描述:

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

示例:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

解题思路:

        思路一:暴力,遍历n次,记下max的值,在内层循环和累加值比较。

        思路二:方法二:分治
                        1.结束条件,当numsSize==1时直接返回当前值
                        2.处理左支求Max_ Left
                        3.处理右支求Max_ Right
                        4.处理本身,向左求最大和Max_ L然后向右求最大和Max_ R
                        5.最终结果为MAX(Max_Left, Max_ Right, Max_ R)

代码实现

        方法一:

    int maxSubArray(vector<int>& nums) {
        int i =0;
        int j = 0;
        int MAX = INT_MIN;
        for(int i = 0; i< nums.size(); i++){
            int temp = 0;
            for(int j = i; j< nums.size(); j++){
                temp += nums[j];
                if(MAX < temp) MAX = temp;
            }
        }
        return MAX;
    }

(实测超时)

        方法二:

#define MAX(a,b,c)  ((a) > ((b)>(c)?(b):(c)) ? (a) : ((b)>(c)?(b):(c))) 

int maxSubArray(int* nums, int numsSize){
    int temp        = 0;
    int i           = 0;
    int MAX_LEFT    = 0;
    int MAX_RIGHT   = 0;
    int MAX_L       = 0;
    int MAX_R       = 0;
    if(numsSize == 0 || numsSize == 1) return nums[0];
    else {
        MAX_LEFT = maxSubArray(&nums[0],(numsSize-1)/2);
        MAX_RIGHT = maxSubArray(&nums[(numsSize+1)/2],numsSize/2);
    }

    MAX_L = nums[(numsSize-1)/2];
    temp = 0;
    for(i = (numsSize-1)/2; i >= 0; i--){
        temp += nums[i];
        MAX_L = MAX_L>temp?MAX_L:temp;
    }
    MAX_R = MAX_L;
    temp = MAX_L;
    for(i =(numsSize+1)/2; i < numsSize; i++ ){
        temp+=nums[i];
        MAX_R = MAX_R>temp?MAX_R:temp;
    }

    return MAX(MAX_LEFT,MAX_RIGHT,MAX_R);
}

原地移除元素

链接:27. 移除元素     27. 移除元素 - 力扣(Leetcode)

题目描述:

给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

解题思路:

        思路:一个(慢) num指针为数组本身进行复写,一个(快) p指针不断移动并判断是否等于val的值。一旦快指针所指向的值不等于val ,则进行复写,将快指针所指向的值复写慢指针所指向的值,且计数器加一。
(注意顺序:先*nums=p[i] ,后nums++ ;因为慢指针的最后一项总是待复写的位置,所以若快指针所
指向的值不等于val时,先将快指针的值复写慢指针的值,再将慢指针往后挪一位)

代码实现

int removeElement(int* nums, int numsSize, int val){
    int count = 0;int slow = 0;
    for(int i = 0;i<numsSize;i++){
        if(nums[i] != val){
            nums[slow] = nums[i];
            slow ++;
            count ++;
        }
    }
    return count;
}

合并两个有序数组

链接:88. 合并两个有序数组   88. 合并两个有序数组 - 力扣(Leetcode)

题目描述:

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

示例:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

解题思路:

        思路一:由于数组1有足够空间,分别从末端遍历存入数组1。

代码实现

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
    int M   = m-1;
    int N   = n-1;
    int cur = m+n-1;
    while(M >= 0 && N >= 0){
        if(nums1[M] >= nums2[N]){
            nums1[cur--] = nums1[M--];
        }else {
            nums1[cur--] = nums2[N--];
        }
    }
    while(N>=0){
        nums1[cur--] = nums2[N--];
    }   
}

查找常用字符

链接:1002. 查找共用字符   88. 合并两个有序数组 - 力扣(Leetcode)1002. 查找共用字符 - 力扣(Leetcode)88. 合并两个有序数组 - 力扣(Leetcode)

题目描述:

        给你一个字符串数组 words ,请你找出所有在 words 的每个字符串中都出现的共用字符( 包括重复字符),并以数组形式返回。你可以按 任意顺序 返回答案。例如,如果一个字符在每个字符串中出现3次,但不是4次,则需要在最终答案中包含该字符3次。

示例:

输入:words = ["bella","label","roller"]
输出:["e","l","l"]

解题思路:

        思路一:维护两个数组,其中一个用于统计每个字符串的字母频率,另一个用以更新最小值,最后输出char **;

代码实现

char ** commonChars(char ** words, int wordsSize, int* returnSize){
    int minfreq[26], freq[26];
    for(int i = 0; i< 26;++i){
        minfreq[i] = INT_MAX;
        freq[i] = 0;
    }
    for(int i = 0;i<wordsSize;++i){
        memset(freq,0,sizeof(freq));
        int n = strlen(words[i]);
        for(int j =0; j<n; ++j){
            ++freq[words[i][j] - 'a'];
        }
        for(int j = 0;j<26; ++j){
            minfreq[j] = fmin(minfreq[j],freq[j]);
        }
    }

    int sum = 0;
    for(int i=0; i< 26;++i){
        sum += minfreq[i];
    }

    char** ans = malloc(sizeof(char*) * sum);
    *returnSize = 0;
    for(int i = 0; i<26; ++i){
        for(int j = 0; j<minfreq[i]; ++j){
            ans[*returnSize] = malloc(sizeof(char) * 2);
            ans[*returnSize][0] = i + 'a';
            ans[*returnSize][1] = 0;
            (*returnSize)++;
        }
    }
    return ans;
}

寻找数组的中心索引

链接:724. 寻找数组的中心下标   724. 寻找数组的中心下标 - 力扣(Leetcode)

题目描述:        

给你一个整数数组 nums ,请计算数组的 中心下标 

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。

示例:

输入:nums = [1, 7, 3, 6, 5, 6]
输出:3
解释:
中心下标是 3 。
左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 ,
右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。

解题思路:

        思路一:滑动窗口 先设定一个初始位置0(坑啊,题目出的有问题,应该从1开始才对,没办法,题目
认为0左边没元素也符合) 以其为中心分别计算左、右的和,然后比较不相等就右移,更新左、右和
即可。

代码实现

int pivotIndex(int* nums, int numsSize){
    int left    = 0;
    int right   = 0;
    for(int i = 0;i<numsSize;++i){
        right += nums[i];
    }    
    right -= nums[0];
    if(right == 0) return 0;
    for(int i = 1;i<numsSize; ++i){
        left += nums[i-1];
        right -= nums[i];
        if(left == right) return i;
    }
    return -1;
}

找出数组中两个无重复的数字

链接:剑指 Offer 56 - I. 数组中数字出现的次数  剑指 Offer 56 - I. 数组中数字出现的次数 - 力扣(Leetcode)

题目描述:        

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

示例:

输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]

解题思路:

        思路:利用异或^运算的特性

                                1、0和任何数异或等于该数
                                2、任何数和自身异或等于0
                                3、异或运算满足交换律

        1、当将一个数组的数全部按位异或后,可以将成对的数放在前面先运算,而成对的数经过上述1,2会得到0 ,也就达到了消去成对的数的目的。此时数组中剩下两个不同的数异或,得到的结果mask是这两个数不同的位上值为1 , 相同的为0.
        2、但这样没办法把两个数分离出来。既然这两个不同的数必然有位是不同的,并且异或的结果也告诉我们哪些位不同。我们不妨以mask的最低的值为1的位(设为a0位)来区分这两个数,其中一个数a0位等于1 ,另一个等于0。其中通过位运算x & (-x)得到a0= 1 ,其他位为0的结果。
       3、 同样我们将数组中其他的数,也分成两组: a0 = 0组和a0= 1组,这样保证了两个不同的数分在两组,也保证了成对的数必在其中一组而不会一对数分在两组。
        4、分别对两组的数进行异或运算,最后得到的两个数即所求。

代码实现

int* singleNumbers(int* nums, int numsSize, int* returnSize){
    int mask = 0;
    for(int i = 0; i< numsSize; ++i){
        mask ^= nums[i];  
    }
    mask = mask & (-mask);
    int m1 = 0;
    int m2 = 0;
    for(int i = 0; i< numsSize; ++i){
        if(nums[i] & mask){
            m1 ^= nums[i];
        }else{
            m2 ^= nums[i];
        }
    }
    *returnSize = 2;
    int *a = (int *)malloc(2*sizeof(int));
    a[0] = m1;
    a[1] = m2;
    return a;
}

找出数组中三个无重复的数字

链接:

剑指 Offer 56 - II. 数组中数字出现的次数 II

剑指 Offer 56 - I. 数组中数字出现的次数 - 力扣(Leetcode)剑指 Offer 56 - II. 数组中数字出现的次数 II - 力扣(Leetcode)剑指 Offer 56 - I. 数组中数字出现的次数 - 力扣(Leetcode)

题目描述:        

在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

示例:

输入:nums = [3,4,3,3]
输出:4

解题思路:

        理论基础:某一数字出现3次,则他们每-位的和分别可以被3整除如数字7 : 0111 ,第0位为1 , 3个1加起来为11 ,可以被三整除。第二第三位同理。
        步骤:
        1.分别给数组中所有数字不同位分别求和,结果记录为temp , int型要分别求32次。
        2.若第i位的求和结果temp能被3整除,说明只出现了1次的那个数字该位为0 ,否则为1。
        3.根据第二部结果,给result的第i位置1或0。( 置0可以不做处理)。

代码实现

int singleNumber(int* nums, int numsSize){
    int result = 0;
    for(int i = 0; i< 32; ++i){
        int temp = 0;
        for(int j = 0; j<numsSize; ++j){
            temp += (nums[j]>>i)&1;
        }
        if(temp % 3) result+=(1<<i);
    }
    return result;
}   

数组中缺失的元素

链接:

268. 丢失的数字268. 丢失的数字 - 力扣(Leetcode)

题目描述:        

给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

示例:

输入:nums = [3,0,1]
输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中

解题思路:

        1、哈希数组

        2、异或

        3、求和取差

代码实现

        方法一

int missingNumber(int* nums, int numsSize){
    int hash[10000] = {0};
    for(int i = 0; i<numsSize; ++i){
        hash[nums[i]]++;
    }
    for(int i = 0;i<=numsSize; ++i){
        if(hash[i] == 0) return i;
    }
    return -1;
}

        方法二

int missingNumber(int* nums, int numsSize){
    int res = numsSize;
    for(int i = 0; i< numsSize; ++i){
        res ^= nums[i];
        res ^= i;
    }
    return res;
}

        方法三

int missingNumber(int* nums, int numsSize){
    int Sum = (numsSize+1)*numsSize/2;
    for(int i =0; i< numsSize;++i){
        Sum -= nums[i];
    }
    return Sum;
}

        测试结果哈希表的时间空间使用是最小的,原因可能是哈希表不需要完全遍历。

按奇偶排序数组

链接:

905. 按奇偶排序数组905. 按奇偶排序数组 - 力扣(Leetcode)

题目描述:        

给你一个整数数组 nums,将 nums 中的的所有偶数元素移动到数组的前面,后跟所有奇数元素。

返回满足此条件的 任一数组 作为答案。

示例:

输入:nums = [3,1,2,4]
输出:[2,4,3,1]
解释:[4,2,3,1]、[2,4,1,3] 和 [4,2,1,3] 也会被视作正确答案。

解题思路:

          维护头尾两个下标,遍历原数组,将偶数从前往后放,奇数从后往前放。

代码实现

int* sortArrayByParity(int* A,int ASize,int* returnSize) {
  *returnSize = ASize;
  if (ASize< 2) return A;
  int *arr = (int *)malloc(sizeof(int)*ASize);
  int head = 0,tail = ASize-1;
  for(int i = 0;i<ASize;i++){
    if(A[i]%2==0)
      arr[head++] = A[i];
    else
      arr[tail--] = A[i];
  }
  return arr;
}

数组是否存在重复元素

链接:

219. 存在重复元素 II219. 存在重复元素 II - 力扣(Leetcode)

题目描述:        

给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 i 和 j ,满足 nums[i] == nums[j] 且 abs(i - j) <= k 。如果存在,返回 true ;否则,返回 false 。

示例:

输入:nums = [1,2,3,1], k = 3
输出:true

输入:nums = [1,0,1,1], k = 1
输出:true

解题思路:

          使用uthash.h头文件,构建C哈希表。(模板)

代码实现

typedef struct hash{
    int key; //键
    int index; //索引值
    UT_hash_handle hh; //让结构体哈希柄
} *hash_ptr;

bool containsNearbyDuplicate(int* nums, int numsSize, int k){
    hash_ptr p=NULL,tables =NULL ;
    for(int i=0;i <numsSize;i++){
        if(tables) HASH_FIND_INT(tables, &(nums[i]),p);
        //如果哈希表中已经存在这个元素,判断当前正在放入的和已经放入的索引值的差值
        if(p&&(i-p->index)<=k)  return true;
        p=(hash_ptr)malloc(sizeof(*p));
        p->key=nums [i] ;
        p->index=i ;
        HASH_ADD_INT(tables,key, p);
    }
    return false;
}

有序数组出现次数超过25%元素

链接:

1287. 有序数组中出现次数超过25%的元素1287. 有序数组中出现次数超过25%的元素 - 力扣(Leetcode)

题目描述:        

给你一个非递减的 有序 整数数组,已知这个数组中恰好有一个整数,它的出现次数超过数组元素总数的 25%。

请你找到并返回这个整数

示例:

输入:arr = [1,2,2,6,6,6,6,7,10]
输出:6

解题思路:

        方法一:将每个元素放进数组,遍历哈希表求次数相等的元素。
        方法二:数组有序,且某元素出现次数超过25%。那么对于此元素第1次出现位置,加25%数组长度,必定仍为它自身。

代码实现

        方法一

int findSpecialInteger(int* arr, int arrSize){
    if(arrSize < 1) return-1;
    int flag[100001] = {0};
    for(int i = 0; i<arrSize; ++i){
        flag[arr[i]]++;
    }
    int cnt = arrSize*0.25;
    for(int i = 0; i<=100000; ++i){
        if(flag[i] > cnt) return i;
    }
    return -1;
}

        方法二

int findSpecialInteger(int* arr, int arrsize){
    for(int*p=arr,*q=arr+arrsize/4;;p++,q++)
    if (*p == *q) return *p;
    return 0;
}

有效的山脉数组

链接:

941. 有效的山脉数组941. 有效的山脉数组 - 力扣(Leetcode)

题目描述:        

给定一个整数数组 arr,如果它是有效的山脉数组就返回 true,否则返回 false

让我们回顾一下,如果 arr 满足下述条件,那么它是一个山脉数组:

  • arr.length >= 3
  • 在 0 < i < arr.length - 1 条件下,存在 i 使得:
    • arr[0] < arr[1] < ... arr[i-1] < arr[i]
    • arr[i] > arr[i+1] > ... > arr[arr.length - 1]

示例:

输入:arr = [2,1]
输出:false

解题思路:

        方法:遍历一次:上山步数+下山步数 = 总长-1

代码实现

bool validMountainArray(int* arr, int arrSize){
    if(arrSize < 3) return false;
    int dec = 0;
    int inc = 0;
    for(int i = 0; i< arrSize-1; ++i){
        if(arr[i]<arr[i+1] && dec==0) inc++;
        else if(arr[i]>arr[i+1] && inc>0) dec++;
    }
    if(inc + dec == arrSize-1 && inc<arrSize-1 && dec<arrSize-1 && inc && dec)  return true;
    return false;   
}

最长连续递增序列

链接:

674. 最长连续递增序列674. 最长连续递增序列 - 力扣(Leetcode)

题目描述:        

给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。

连续递增的子序列 可以由两个下标 l 和 rl < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列。

示例:

输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。

解题思路:

     维护一个递增子数组,分别统计其长度,比较取最大值。

代码实现

int findLengthOfLCIS(int* nums, int numsSize){
    int mlen = 1;
    for(int i = 0 ; i<numsSize-1;i++){
        int len = 1;
        while(i<numsSize-1 && nums[i] < nums[i+1]){
            len++;
            i++;    
        }
        mlen = len>mlen?len:mlen;
    }
    return mlen;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值