算法随笔

原地删除重复的数字

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

你不需要考虑数组中超出新长度后面的元素。

实现


int remove_duplicates(int a[],int size){

    int index = 0;
    for (int i=0;i

数组中出现次数超过一半的元素

数组(无序)中有一个数字出现的次数超过了数组长度的一半,找出这个数字。

解法1

如果数据量小,可以对数组进行排序,那么数组中间n/2的数就是出现次数超过一半的数,复杂度O(nlogn)

解法2

每次删除数组中两个不同的元素,删除后,要查找的那个元素的个数仍然超过删除后的元素总数的一半

  • 需要两个辅助参数,一个是出现的数值,一个是该数值出现的次数

template 
void FindOneNumber(T a[],int size){
    if( size <= 0) return -1;
    int nTimes = 1;
    int pre = a[0];

    for(int i=1;i

将数值向右移动K个位置(非负数)

解法1

每次遍历数组,可以向后移动一位,移动k个位置,则需要k次遍历,O(k*n)

void move_k(int a[],int size,int k){
    // 求需要移动的最小步数,
    int loca = size%k;
    for(int i=0;i=1;j--){
            a[j] = a[j-1];
        }
        a[0] = tmp;
    }
}
# 时间复杂度 O(k*n)  size/k  

解法2

借助O(n)的空间,将数组复制到新数组中,然后遍历重新赋值即可,时间O(n)
string 形参,可以使用引用const string& str,减少内存的拷贝

void move_k(int a[],int size,int k){
    int loca = size%k;
    int tmp[size];
    # C风格的数组复制方法(memset etc.)
    memcpy(tmp,a,size*sizeof(int));

    for(int i=0;i

判断重复数字

长度为n的数组,赋值为1~n,判断是否存在重复元素

这个数组的特性是,1~n每个值都使用一次才会不重复,所以我们可以将数组对应位置设置为对应的值,去判断是否为冲突


//遍历数组,假设第i个位置的数字为j,则通过交换将j换到下标为j的位置上,直到所有数字都出现在自己对应的下表处,或发生了冲突。
//时间复杂度:O(n),空间复杂度:O(1)

bool find_dup(int a[],int size){
    if(size<=1) return false;
    
    for(int i=0;i

变形2

  • 普通的数组,则可以通过排序,然后判断前后元素是否相同来确定是否有重复元素
  • 或者利用STL库的set容器,它保存有序的无重复的数组,支持插入,删除,查找等操作,所有的操作的都是严格在logn时间之内完成,效率非常高
return nums.size() > set(nums.begin(),nums.end()).size();

找出只出现一次的元素

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。(线性复杂度)

解法1

因为其它元素都出现两次,只有一个出现一次,就可以将数组先排序,然后以步长2遍历一次数组,找到 a[i] != a[i+1] 的即可

解法2

可以使用异或运算进行判定,由于都是出现偶数次,而0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0,且异或满足交换律,所以可以对整个数组进行异或运算,最后得到的结果即为单次出现的数字

int arr[5] = {4,2,1,2,4}
4⊕2⊕1⊕2⊕4 = 4⊕4⊕2⊕2⊕1 = (0)⊕(0)⊕1 = 1

实现

    int find_dup2(int a[],int size){
        int temp = 0;
        for(int i=0;i

求数组的交集

  • 给定两个数组,编写一个函数来计算它们的交集。
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [4,9]

输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]

说明:

  • 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
  • 我们可以不考虑输出结果的顺序。

进阶:

  • 如果给定的数组已经排好序呢?你将如何优化你的算法?
  • 如果 nums1 的大小比 nums2 小很多,哪种方法更优?
  • 如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?

解法1

# 最暴力的方法,时间O(n^2)  
void find_jiao(int a[],int b[],int size_a,int size_b){
    vector c;
    
    for(int i=0;i

排序数组,找交集

如果两个数组是有序的,则可以分别设置一个索引,如果a[index1]

vector find_jiao(int a[],int b[],int size_a,int size_b){
    int index1 = 0,index2 = 0;
    vector c;

    shell_sort(a,size_a);
    shell_sort(b,size_b);

    while(index1 b[index2]){
            index2++;
        }
        else{
            c.push_back(a[index1]);
            index1++;
            index2++;
        }
    }
    return c;
}

Plus One 加1

给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储一个数字,你可以假设除了整数 0 之外,这个整数不会以零开头。

输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。

实现

void plus_one(int a[],int size){

    for(int i=size-1;i>=0;i--){
        if(a[i]<9 ){
            a[i]+=1;
            break;
        }else{
            a[i]=0;
            i++;
        }
    }
}

移动所有的0到末尾

使用一个索引标志,遍历一次数组,遍历的同时将非0的元素交换到标志索引处,最后将剩余位置填充0即可!

void move_zero(int a[],int size){
    int i=0;

    for(int x=0;x

反转字符串

输入: "A man, a plan, a canal: Panama"
输出: "amanaP :lanac a ,nalp a ,nam A"

void reverseString(string& str){
    int i=0,j=str.size()-1;
    while(i

字母异位词

利用了字母易位词即为各个字母的数目相同,而顺序不一致。我们从另外一个角度思考,字母一共有多少个?很明显,只有26个(只考虑小写字母)。那么,我们可以为字符串s1和s2分别设置26个计数器,然后判断这对应位置的计数是否相等,如果对应计数完全相等,则为字母易位词

输入: s = "anagram", t = "nagaram"
输出: true

实现

bool isAnagram(string s, string t) {
    if( s.size() != t.size()) return false;
    
    int cnt1[26],cnt2[26];
    memset(cnt1,0,26* sizeof(int));
    memset(cnt2,0,26* sizeof(int));
    
    for(int i=0;i

回文字符串

/********11. 回文字符串 **************/

bool isPalindrome(const string& s) {
   
        int i=0, j=s.size()-1;
        while(i

查找字符串数组最长前缀

例:
输入: [“flower”,”flow”,”flight”] 输出: “fl” 示例 2:
输入: [“dog”,”racecar”,”car”] 输出: “” 解释: 输入不存在公共前缀。 说明:
# 所有输入只包含小写字母 a-z 
string longestCommonPrefix(vector& strs) {
        string res;
        if(strs.empty()){
            return "";
        }
        for(int i=0;istrs[j].size()-1||c!=strs[j][i]){
                    return res;
                }
            }
            res.push_back(c);
        }
        return res;

    }

合并两个有序链表

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

实现

ListNode* Merge_two(ListNode *l,ListNode* r){
    if(l->next==NULL && r->next==NULL) return NULL;

    ListNode dumpy(0);
    ListNode* tmp=&dumpy;

    while(l->next!=NULL && r->next!=NULL){
        if(l->val > r->val){
            tmp->next = r;
            r = r->next;
        }else{
            tmp->next = l;
            l = l->next;
        }
        tmp = tmp->next;
    }

    if(l!=NULL){
        tmp->next=l;
    }
    if(r!=NULL){
        tmp->next=r;
    }

    return tmp.next;
}

合并有序数组

  • 给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。

初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3
输出: [1,2,2,3,5,6]

实现



数组最大子序和

输入: {1, -2, 3, 10, -4, 7, 2, -5},
输出: 18
解释: 最大的子数组为{3, 10, -4, 7, 2},和为18

解法1

# O(n^2)  遍历数组的每一个子序列,记录最大值
void maxSubArray(int a[],int size) {
   int sumMax = 0;
   for(int l=0;ltmp?sumMax:tmp;
       }
   }
}

解法2

我们试着从头到尾逐个累加示例数组中的每个数字。初始化和为 0。第一步加上第一个数字 1, 此时和为 1。接下来第二步加上数字 -2,和就变成了 -1。第三步刷上数字3。我们注意到由于此前累计的和是 -1 ,小于 0,那如果用-1 加上 3 ,得到的和是 2 , 比 3 本身还小。也就是说从第一个数字开始的子数组的和会小于从第三个数字开始的子数组的和。因此我们不用考虑从第一个数字开始的子数组,之前累计的和也被抛弃。

我们从第三个数字重新开始累加,此时得到的和是 3。接下来第四步加 10,得到和为 13 。第五步加上 -4, 和为 9。我们发现由于 -4 是一个负数,因此累加 -4 之后得到的和比原来的和还要小。因此我们要把之前得到的和 13 保存下来,它有可能是最大的子数组的和。第六步加上数字 7,9 加 7 的结果是 16,此时和比之前最大的和 13 还要大, 把最大的子数组的和由 13 更新为 16。第七步加上 2,累加得到的和为 18,同时我们也要更新最大子数组的和。第八步加上最后一个数字 -5,由于得到的和为 13 ,小于此前最大的和 18,因此最终最大的子数组的和为 18 ,对应的子数组是{3, 10, -4, 7, 2}。

  • 根据数组的规律,去进行逐个累加,废弃对最大和没有作用的子序列

实现

void maxSubArray(int a[],int size){
    int cur=0;
    int max=0;
    for(int i=0; i0?cur:0;
        // 更新最大的子序列和 
        max = max>cur?max:cur;
    }
}

何时买卖股票_121

完成一次交易

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。注意你不能在买入股票前卖出股票

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第2天(股票价格 = 1)的时候买入,在第5天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5,注意利润不能是7-1 =6;

输入:[7,6,4,3,1] 这种情况输出0

分析:

其实这个和上面的最大序列和一样,我们可以将价格序列相减,可以得到[0,-6,4,-2,3,-2],然后我们从这个序列中找最大子序列和即可,[4,-2,3]=5即为最大收益

# Time O(n) Space O(n)
void MaxProfit(int a[],int size){

    int buff[size-1]; # 存储收益差
    for(int i=0;i0?cur:0;
        max = max>cur?max:cur;
    }
}

#  或者不另外开辟空间存储收益差也可以,改动如下:  Space o(1)
    for(int i=1;icur?max:cur;
    }

可以完成多次交易

**唯一的区别在于 上面是求最大利润 这个是求有效利润和 sum+=temp **

class Solution {
public:
    int maxProfit(vector& prices) {
        int sum=0;
        if(prices.size()==0) return 0;
        for(int i=0;i0)
                sum+=temp;
        }
        return sum;
    }
};

Pascal's Triangle__118

class Solution {
public:
    vector> generate(int numRows) { 
        vector> result;
        
        for(int i=0;i x(1,1);
                result.push_back(x);
            }
            else if(i==1) {
                vector x(2,1);
                result.push_back(x);
            }
            else{
                vector buff;
                buff.push_back(1);
                for(int j=0;j

多层vector容器的赋值问题

vector> v;
vector buff;
# 由于最外层的vector里放着更小的vector,所以需要先赋值里层的vector,再将里层vector作为元素push 到外层vector中

//输入
    for (int i = 0; i> temp;
            buff.push_back(temp);
        }
        v.push_back(buff);
    }

Max Consecutive Ones__485.

给定二进制数组,找到此数组中连续1的最大数量

Input: [1,1,0,1,1,1]
Output: 3
Explanation: The first two digits or the last three digits are consecutive 1s.
    The maximum number of consecutive 1s is 3

实现

class Solution {
public:
    int findMaxConsecutiveOnes(vector& nums) {
        int size = nums.size();    
        int max=0,cur=0;
        for(int i=0;icur?max:cur;
        }
        return max;
    }
};

第三个大数字 Third Maximum Number__414

给定非空的整数数组,返回此数组中的第三个最大数字。 如果不存在,则返回最大数量。 时间复杂度必须是O(n)

Input: [3, 2, 1]
Output: 1

Input: [1, 2]
Output: 2  max

实现

 int thirdMax(vector& nums) {
        int size = nums.size();        
        double max0=-1*DBL_MAX,max1=-1*DBL_MAX,max2=-1*DBL_MAX;
        
        for(int i=0;imax0){
                max2 = max1;
                max1 = max0;
                max0 = nums[i];
            }else if(nums[i]>max1 && nums[i]max2 && nums[i]

找连续分组的位置 Positions of Large Groups__830

给定字符串,找出包含3及以上的所有分组的位置索引

Input: "abbxxxxzzy"
Output: [[3,6]]
Explanation: "xxxx" is the single large group with starting  3 and ending positions 6.

Input: "abc"
Output: []
Explanation: We have "a","b" and "c" but no large group.

Input: "abcdddeeeeaabbbcd"
Output: [[3,5],[6,9],[12,14]]

实现

  vector> largeGroupPositions(string S) {
        vector buf;
        vector> result;
        int cnt=1;
        int size = S.size();
        for(int i=0;i=3){
                    buf.push_back(i-cnt+1);
                    buf.push_back(i);
                    result.push_back(buf);
                }
                buf.clear();
                cnt=1;
            }    
        }
        return result;
    }

找数组左右和相等的中间索引 Find Pivot Index__724

Input: 
nums = [1, 7, 3, 6, 5, 6]
Output: 3

实现

class Solution {
public:
    int pivotIndex(vector& nums) {
        int size = nums.size();
        if(size<=2) return -1;
        int pre=0;
        
        long lsum=0,rsum=0;
        for(auto i:nums) lsum+=i;
        
        for(int i=0;i

爬台阶的方法 Climbing Stairs _70

Input: 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step

类似于斐波那契数列  a(n) = a(n-1) + a(n-2);

实现

int climbStairs(int n) {
        if(n < 1){
            return 0;
        }
        if(n < 3){
            return n;
        }
        int first = 2;
        int second = 1;
        int current = 0;
        for(int i = 3; i <= n; ++i){
            current = first + second;
            second = first;
            first = current;
        }
        return current;
    }
    

二进制加法 Add Binary_67(leetcode)


Given two binary strings, return their sum (also a binary string).

Input: a = "1010", b = "1011"
Output: "10101"

The input strings are both non-empty and contains only characters 1 or 0.

实现

string addBinary(string a, string b) {
        int s1 = a.size() - 1, s2 = b.size() - 1;
        int maxlen = max(s1,s2);
        int carry = 0;
        string s(maxlen + 1, '0');
         
        if (s1 < s2) {
            while (s1 < s2) {
                s1++;
                a.insert(a.begin(), '0');
            }
        } else if (s2 < s1) {
            while (s2 < s1) {
                s2++;
                b.insert(b.begin(), '0');
            }
        }
        
        while (maxlen >= 0) {
            int sum = (a[maxlen] - 48) + (b[maxlen] - 48)+ carry;
            
            if (sum == 3 || sum == 1)
                s[maxlen] = '1';
            else
                s[maxlen] = '0';
            
            if (sum == 2 || sum == 3)
                carry = 1;
            else
                carry = 0;
            
            maxlen--;
        }
        
        if(carry==1)
             s.insert(s.begin(),'1');
        
        return s;
        

对称二叉树 101. Symmetric Tree

可以使用树的层次遍历 但是有一个问题,不知道每层存在多少元素,可以使用队列的size进行判断, 或者 使用两个队列 分别保存当前层元素和 下一层元素,当当前层队列为空时,即为遍历完一层,然后交换两个队列 ,两个实现分别为 1 2

解法1

vector> levelOrderBottom(TreeNode* root) {
    vector temp;
    vector> res;
    
    if(root == NULL) return res;
    queue q;
    
    q.push(root);
    temp.push_back(root->val);
    res.push_back(temp);
    temp.clear();
    
    while(!q.empty()){
        
        int size = q.size();
        
        while(size>0){
            TreeNode* a = q.front();
            q.pop();
            if(a->left!=NULL)
            {
                q.push(a->left);
                temp.push_back(a->left->val);
            }
            if(a->right!=NULL)
            {
                q.push(a->right);
                temp.push_back(a->right->val);
            }
            
            size--;
        }
        if(temp.size()>0)
            res.push_back(temp);
        temp.clear();
        
    }   
    reverse(res.begin(),res.end());
    return res;    
}

解法2

vector> levelOrderBottom(TreeNode* root) {
    vector temp;
    vector> res;        
    queue q_cur,q_next;
    
    q_cur.push(root);
    temp.push_back(root->val);
    res.push_back(temp);
    temp.clear();
    
    while(!q_cur.empty()){
        TreeNode* a = q_cur.front();
        q_cur.pop();
        if(a->left!= NULL){
            q_next.push(a->left);
            temp.push_back(a->left->val);
        }
        if(a->right!=NULL){
            q_next.push(a->right);
            temp.push_back(a->right->val);    
        }
        
        if(q_cur.empty()){
            swap(q_cur,q_next);
            if(temp.size()>0)
                res.push_back(temp);
            temp.clear(); 
        }
        
    }
    
 reverse(res.begin(),res.end());
 return res;
   
}

二叉树的层次反转 107 Binary Tree Level Order Traversal II

给定一个二叉树,返回它从叶子到根节点的数值遍历

image

实现

可以使用树的层次遍历 但是有一个问题,不知道每层存在多少元素,可以使用队列的size进行判断, 或者 使用两个队列 分别保存当前层元素和 下一层元素,当当前层队列为空时,即为遍历完一层,然后交换两个队列 ,两个实现分别为 1 2

解法1


    vector> levelOrderBottom(TreeNode* root) {
        vector temp;
        vector> res;
        
        if(root == NULL) return res;
        queue q;
        
        q.push(root);
        temp.push_back(root->val);
        res.push_back(temp);
        temp.clear();
        
        while(!q.empty()){
            
            int size = q.size();
            
            while(size>0){
                TreeNode* a = q.front();
                q.pop();
                if(a->left!=NULL)
                {
                    q.push(a->left);
                    temp.push_back(a->left->val);
                }
                if(a->right!=NULL)
                {
                    q.push(a->right);
                    temp.push_back(a->right->val);
                }
                
                size--;
            }
            if(temp.size()>0)
                res.push_back(temp);
            temp.clear();
            
        }   
        reverse(res.begin(),res.end());
        return res;    
    }

实现2

    vector> levelOrderBottom(TreeNode* root) {
        vector temp;
        vector> res;        
        queue q_cur,q_next;
        
        q_cur.push(root);
        temp.push_back(root->val);
        res.push_back(temp);
        temp.clear();
        
        while(!q_cur.empty()){
            TreeNode* a = q_cur.front();
            q_cur.pop();
            if(a->left!= NULL){
                q_next.push(a->left);
                temp.push_back(a->left->val);
            }
            if(a->right!=NULL){
                q_next.push(a->right);
                temp.push_back(a->right->val);    
            }
            
            if(q_cur.empty()){
                swap(q_cur,q_next);
                if(temp.size()>0)
                    res.push_back(temp);
                temp.clear(); 
            }
            
        }
        
     reverse(res.begin(),res.end());
     return res;
        
    }

叶子节点最小深度 111. Minimum Depth of Binary Tree

返回叶子 (无左右孩子的节点) 节点的最小深度

image
int minDepth(TreeNode* root) {
        if(root==NULL) return 0;
        int dep =1;
        queue q_cur,q_next;
        q_cur.push(root);
        
        while(!q_cur.empty()){
                TreeNode* a = q_cur.front();
                q_cur.pop();
               
               if(a->left==NULL && a->right==NULL) 
                    return dep;
               if(a->left!=NULL)
                   q_next.push(a->left);
               if(a->right!=NULL)
                   q_next.push(a->right);
            
                if(q_cur.empty()){
                    swap(q_cur,q_next);
                    dep+=1;
                    
                }
            }
             return dep;
    }


# 或者使用一个队列  每一次层次遍历 都提前计算出当前层的size   既当前层的元素数量

//         while(!q.empty()){
//             int size = q.size();
//             while(size>0){
//                 TreeNode* a = q.front();
//                 q.pop();
               
//                if(a->left==NULL && a->right==NULL) 
//                     return dep;
//                if(a->left!=NULL)
//                    q.push(a->left);
//                if(a->right!=NULL)
//                    q.push(a->right);
//                size--;
//             }
            
//             dep+=1;
//         }
    

你可能感兴趣的:(算法随笔)