leetcode【数据结构简介】《数组和字符串》卡片——小结

Authur Whywait 做一块努力吸收知识的海绵
想看博主的其他所有leetcode卡片学习笔记链接?传送门点这儿

文章目录

  • 《数组和字符串》卡片- 小结
    • 1. 旋转数组
      • 暴力法
      • 反转
      • 利用最大公约数
      • 使用环状替代
      • 使用辅助数组
    • 2. 杨辉三角II
      • 普通解法
      • 优化解法
    • 3. 翻转字符串里的单词
      • 法一:辅助(返回)字符串+反转
      • 法二:反转-额外空间复杂度O(1)
    • 4. 反转字符串中的单词III
    • 5. 删除排序数组中的重复项
      • 法一:暴力双指针法
      • 法二:优化(快慢指针法)
    • 6. 移动零
      • 快慢指针法

《数组和字符串》卡片- 小结

1. 旋转数组

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]

暴力法

void rotate(int* nums, int numsSize, int k){    
    if(numsSize<1 || k<1 || k%numsSize==0) return;    
    for(int i=0; i<k%numsSize; i++){        
        int temp = nums[numsSize-1];        
        for(int j=numsSize-1; j>0; j--) nums[j] = nums[j-1];        
        nums[0] = temp;    
    }    
    return;
}

执行结果
在这里插入图片描述

反转

/*反转函数*/
void reverse(int* nums, int numsSize, int a, int b){    
    int low=a, high=b;    
    while(low<high){        
        int temp=nums[low];        
        nums[low] = nums[high];        
        nums[high] = temp;        
        high--; low ++;    
    }    
    return;
}
/*rotate函数主体*/
void rotate(int* nums, int numsSize, int k){    
    if(numsSize<1 || k<1 || k%numsSize==0) return;    
    int k0=k%numsSize;    
    reverse(nums, numsSize, 0, numsSize-1);    
    reverse(nums, numsSize, 0, k0-1);    
    reverse(nums, numsSize, k0, numsSize-1);    
    return;
}

leetcode【数据结构简介】《数组和字符串》卡片——小结_第1张图片

利用最大公约数

利用 最大公约数 轻松求解

使用环状替代

使用环状替代

使用辅助数组

O(n)算法,使用辅助数组,不符合题目空间复杂度O(1)的算法要求(虽然官方也给出了这种算法)

2. 杨辉三角II

给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。

输入: 3
输出: [1,3,3,1]

普通解法

int* getRow(int rowIndex, int* returnSize){    
    * returnSize = rowIndex + 1;    
    int** tri = (int **) malloc (sizeof(int*) * (rowIndex+1));    
    for(int i=0; i<rowIndex+1; i++){        
        tri[i] = (int *) malloc (sizeof(int) * (i+1));        
        tri[i][0] = 1;
        tri[i][i] = 1;    
    }    
    for(int i=2; i<rowIndex+1; i++){        
        for(int j=1; j<i; j++){            
            tri[i][j] = tri[i-1][j-1] + tri[i-1][j];        
        }    
    }    
    return tri[rowIndex];
}

leetcode【数据结构简介】《数组和字符串》卡片——小结_第2张图片

优化解法

降低解法的空间复杂度

只申请一行的空间,从后往前操作就不需要考虑被覆盖的问题。

int* getRow(int rowIndex, int* returnSize){    
    * returnSize = rowIndex + 1;    
    int* array = (int *)malloc(sizeof(int) * (rowIndex+1));    
    for(int i=0; i<rowIndex+1; i++){        
        array[i]=1;        
        for(int j=i-1; j>0; j--) array[j] = array[j] + array[j-1];        
        array[0] = 1;    
    }    
    return array;
}

leetcode【数据结构简介】《数组和字符串》卡片——小结_第3张图片

3. 翻转字符串里的单词

给定一个字符串,逐个翻转字符串中的每个单词。

输入: “the sky is blue”
输出: “blue is sky the”

输入: " hello world! "
输出: “world! hello”
解释:输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。

输入: “a good example”
输出: “example good a”
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

法一:辅助(返回)字符串+反转

思路:
1.除了多余空格,其他字符依次转移到辅助字符串里。
2.反转整个字符串,再把单词依次反转。

法二:反转-额外空间复杂度O(1)

思路:
1.遍历字符串,删去多余空格,同时反转单词(使用双指针headtail)。
2.最后反转整个字符串。

代码:

void reverse(char * s, int a, int b){
    while(a<b){
        char temp=s[a];
        s[a++] = s[b];
        s[b--] = temp;
    }
    return;
}
char * reverseWords(char * s){    
    int len=strlen(s), head=0, tail=0;    
    /*删除前面多余的空格*/
    while(s[head]==' ') head++;
    for(int i=0; i<len-head; i++) s[i] = s[i+head];    
    s[len-head] = '\0';    
    len -= head;head = 0;    
    int len1 = len;bool flag;    
    if(!len1) return "";
    
    /*删除中间多余的空格并反转单词(如果最后有空格的话仍有一个空格未处理)*/
    for(int i=0; i<len+1; i++){        
        printf("%d",i);        
        if(i>len1) break;        
        if(i==len1 && flag){//如果字符串末尾没有空格的情况
            tail=i-1;            
            reverse(s, head, tail);        
        }        
        else if(s[i]!=' ' && s[i]!='\0'){
            if(!flag) head=i;	//若为不为空格且前面一个是空格:确定head
            flag=true;        
        }        
        else if(s[i]==' '){            
            if(flag){                
                tail=i-1;	//若为空格且前面一个不为空格:确定tail
                reverse(s, head, tail);                
                flag = false;            
            }            
            else if(!flag){	//若为空格且前面一个也为空格,删除此空格且i--
                len1--;                
                for(int j=i; j<len1; j++) s[j] = s[j+1];                
                s[len1] = '\0';                
                i--;            
            }else;        
        }else;    
    } 
    /*若有空格,处理字符串末尾空格*/
    if(!flag){     
        len1--;        
        s[len1] = '\0';    
    }
    /*反转整个字符串*/
    reverse(s, 0, len1-1);
    return s;
}

细心的读者就会发现了,为什么有一行输出printf("%d",i);呢?因为是直接在leetcode上调试,所以要输出一些东西便于调试。结果在删除的时候遇到了问题,删除这个printf会导致程序的执行结果发生变化,真的是非常amazing了。
最后就把带着printf("%d",i);的代码提交了~

执行结果:在这里插入图片描述

执行时间长确是一个硬伤。

4. 反转字符串中的单词III

给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

输入: “Let’s take LeetCode contest”
输出: “s’teL ekat edoCteeL tsetnoc”

【思路】
翻转字符串里的单词相比就很简单了,思路相同,故直接上代码:

void reverse(char * s, int a, int b){    
    while(a<b){        
        char temp=s[a];        
        s[a++] = s[b];        
        s[b--] = temp;    
        }    
    return;
}
char * reverseWords(char * s){    
    int len=strlen(s), head=0, tail;    
    if(!len) return "";    
    bool flag=true;    
    for(int i=0; i<len; i++){        
        if(s[i]==' '){            
            if(flag){                
                tail = i-1;                
                reverse(s, head, tail);            
            }            
            flag = false;        
        }        
        else if(s[i]!=' '){            
            if(!flag) head = i;            
            flag = true;        
        }    
    }    
    reverse(s, head, len-1);    
    return s;
}

leetcode【数据结构简介】《数组和字符串》卡片——小结_第4张图片

5. 删除排序数组中的重复项

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

给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。

法一:暴力双指针法

思路:遇到重复,所有后面的元素往前移一个

int removeDuplicates(int* nums, int numsSize){
    int len=numsSize;    
    for(int i=1; i<len; i++){        
        if(nums[i]==nums[i-1]){            
            len--;            
            for(int j=i; j<len; j++) nums[j] = nums[j+1];            
            i--;        
        }    
    }    
    return len;
}

法二:优化(快慢指针法)

法一中的遇到重复就把后面全体往前移一个位置,但是如果诸如011111111111112之类的呢?就会多很多无谓的操作。
于是,快慢指针法就应运而生。

int removeDuplicates(int* nums, int numsSize){    
    if(!numsSize) return 0;    
    int slow=1, quick=1;    
    while(quick<numsSize){        
        if(nums[quick]!=nums[quick-1]) nums[slow++] = nums[quick++];        
        else quick++;    
    }    
    return slow;
}

leetcode【数据结构简介】《数组和字符串》卡片——小结_第5张图片

很明显,不管是在执行用时还是在内存消耗方面,快慢指针法都远远优于方法一。

6. 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

快慢指针法

直接用最优的快慢指针法

void moveZeroes(int* nums, int numsSize){    
    int num=0; int slow=0, quick=0;    
    while(quick<numsSize){        
       if(nums[quick]) nums[slow++] = nums[quick++];        
       else{            
           quick++;            
           num++;        
       }   
    }    
    for(int i=numsSize-num; i<numsSize; i++) nums[i]=0;    
    return;
}

leetcode【数据结构简介】《数组和字符串》卡片——小结_第6张图片

你可能感兴趣的:(leetcode卡片学习)