leetcode数组题

 

数组算法

 

完美洗牌算法

c语言随机数如何产生:

利用srand((unsigned int)(time(NULL))是一种方法,因为每一次运行程序的时间是不同的。

在C语言里所提供的随机数发生器的用法:现在的C编译器都提供了一个基于ANSI标准的伪随机数发生器函数,用来生成随机数。它们就是rand()和srand()函数。这两个函数的工作过程如下:

1) 首先给srand()提供一个种子,它是一个unsigned int类型,其取值范围从0~65535;

2) 然后调用rand(),它会根据提供给srand()的种子值返回一个随机数(在0到32767之间)

3) 根据需要多次调用rand(),从而不间断地得到新的随机数;

4) 无论什么时候,都可以给srand()提供一个新的种子,从而进一步“随机化”rand()的输出结果。

下面是0~32767之间的随机数程序:

#include <stdlib.h>
#include <stdio.h>
#include <time.h>//使用当前时钟做种子

void main( void )
{

  int i;
  srand( (unsigned)time( NULL ) ); //初始化随机数
  for( i = 0; i < 10;i++ ) //打印出10个随机数
    printf( " %d\n", rand() );
}

完美洗牌算法:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>//使用当前时钟做种子
    
int main () {
    int arr[10] ={2,5,3,23,5,4,22,44,776,45};
    int temp;
    int index;
        int i;
    srand((unsigned)time(NULL));//初始化随机数
    for(i=10-1;i>=0;i--)
    {    
        temp = arr[i];
        index = rand() %(i + 1);//生成0-i的随机数
        //rand() % 10:生成[0,9]的整数随机数
        //rand() % 10+5:生成[5,14]的整数随机数
        arr[i] = arr[index];
        arr[index] = temp;
    }
    for(i=0;i<10;i++)
    {    
        printf("%d\n",arr[i]);
    }
    return 0;
}

 

窗口大小为K的最大子数组和:利用滑动窗口

#include <stdio.h>

#define k 3 //定义滑动窗口的大小
    
int main () {
    int list[15] = {5,23,86,21,43,67,45,34,58,23,102,123,11,22,1};
    int i = 0;//数组下标
    int j; //滑动窗口的起始头
    int sum = 0;  //临时结果
    int maxsum = sum; //最后的结果
    while(i+k-1<15){for(j = i;j<i+k;j++)
        {
            sum = sum +list[j];
        }
        if(sum>maxsum) maxsum = sum;
        sum = 0;
        i++;
    }
    printf("%d\n",maxsum);
}

 

 

寻找最小的k个数

假定最小的k个数,遍历数组以后的n-k个数,依次替换掉这k个数里的最大值。

#include <stdio.h>
    
#define size 2 //k个数
int arr[10] ={2,5,3,23,5,4,22,44,776,45};    
int minarr[size] = {2,5};

int Getmaxindex(){
    int min = minarr[0];
    int i =1;
    int index = 0;
    for(;i<size;i++)
    {   //找到最大值
        if(minarr[i]>min){
            min = minarr[i];
            index = i;
        } 
    }
    return index;
}    
    
int main () {
    int maxnumindex; //定义每次从minarr返回最大值的下标
    int maxnum; //定义minarr中的最大值
    int i = size -1; //继续从size-1的位置开始遍历往后的元素
    while(i<10){
        //得到最大值下标
        maxnumindex = Getmaxindex(minarr);
        //得到最大值
        maxnum = minarr[maxnumindex];
        //继续遍历
        //如果继续遍历得到的值比minarr数组中最大的值大,将这个最大值替换成这个值
        if(arr[i]<maxnum){
            minarr[maxnumindex] = arr[i];
        }
        i++;
    }
    for(i = 0; i<size;i++){
        printf("%d\n",minarr[i]);
    }
    return 0;
}

 

 

寻找和为定值的两个数:有序数组与双向滑动指针

#include <stdio.h>
int main () {
    int sum = 124;
    int list[15] = {5,23,86,21,43,67,45,34,58,23,102,123,11,22,1};
    
    //对数组进行选择排序:从小到大////////
    int i, j, temp,min;
    for(i = 0; i< 15;i++)
    {
//令最小值等于无序区的第一个值
        min = list[i];
//找到无序区的最小值
        for(j = i; j<15;j++)
        {
            if(min>list[j])
            {
                temp = min;
                min = list[j];
                list[j] = temp;
            }
        }
        list[i] = min;
    }
    //for (i = 0;i < 15; i++)
       // printf("%d\n",list[i]);
    /////////////////////
    
    ///有序:从小到大的序列中找到满足x+y =sum的x与y元素//
    int start = 0;
    int end = 14;
    int currsum = 0;
    while(start<end){
        currsum = list[start] +list[end];
        if(currsum == sum)
        {
          printf("currsum == sum:%d+%d=%d\n",list[start],list[end],currsum);
          //i++;
          //j--; //寻找其它x与y可以去掉这两行注释
          break;
        }else{
            //如果currsum比sum小,只需要移动start指针
            if(currsum < sum){start++;} 
            //如果currsum比sum大,只需要移动end指针
            else{end--;}     
        }
    }
}

 

数组中出现次数超过一半的数字

如果一个数出现的次数超过数组一半的长度,那么就是说出现的次数比其他所有数字出现的次数还要多。因此我们可以考虑保存2个值,一个是数组中的一个数,一个是数的次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1,如果不同则次数减1。如果次数为0了这保存当前遍历到的数,并把次数设为1。遍历完整个数组之后,返回当前保存的数字,即是我们要找的数字。

#include <stdio.h>

int numbers[] = {3,3,2};
int result = 3;    

int MoreThanHalfNum(int length)
{    
    
    int count = 1 ;
    int i;
    //从下标为1开始遍历
    for (i = 1 ;i < length ; i++)
    {
       
        if (numbers[i] == result)
        {
            count++;
        }
        else 
        {
            count-- ;
        }
         if (count == 0)
        {
            result = numbers[i] ;
            count++ ;
        }
        
    }
    return count;
}
int main()
{
    int count = MoreThanHalfNum(3);
    if(count>0){
        printf("出现次数超过一半:%d",result);
    }else if(count==0){
        printf("出现次数与其它所有数相等:%d\n",result);
    }else{
        printf("没有");
    }
    return 0;
}

 

 

 

寻找二维数组递增数组的一个数

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

#include <stdio.h>
    
int matrix[5][5] = {
                {1,4,7,11,15},
                {2,5,8,12,19},
                {3,6,9,16,22},
                {10,13,14,17,24},
                {18,21,2326,30},
        };
int find(int num){
        int r = 0,c = 5 - 1;//从右上角开始
        while (r <= 5 - 1 && c >= 0) {
            if (num == matrix[r][c]) {
                return 1;
            } else if (num > matrix[r][c]) {
                r++;
            } else {
                c--;
            }
        }
        return 0;
}    
    
    
    
int main () {
    int flag = find(17);
    printf("%d\n",flag);
    return 0;
}

 

 

奇偶数排序

  给定一个整数数组,请调整数组中数的顺序,使得所有奇数位于数组的前半部分,偶数位于后半部分。

使用两个指针,一个指向头,一个指向尾,两个指针从数组头部和尾部向数组中间一段,如果头指针指向的是偶数,尾指针指向的是奇数,就交换两个数。

#include <stdio.h>


//判断奇偶数
int isoaddnumber(int num){
    return num&1;
}

//
void OddEvenSort(int *pData,int length){
    //头指针
    int *pstart = pData;
    //尾指针
    int *pend = pData + length - 1;
    int temp;
    while(pstart < pend){
        //如果pstart指针指向的时奇数,正常,向右移
        if(isoaddnumber(*pstart)){
            pstart ++;
        }
        //如果pend指针指向的时偶数,正常,向左移
        else if(isoaddnumber(*pend)==0){
            pend --;
        }
     //都不正常则交换两个数
else{ temp = *pstart; *pstart = *pend; *pend = temp; } } } int main() { int numbers[] = {3,2,8,56,34,5,9,32,23}; int i =0; OddEvenSort(numbers,9); for(;i<9;i++) printf("%d\n",numbers[i]); return 0; }

 

荷兰国旗

将n个红白蓝标题颜色的小球排序为,红、白、蓝,假设红为0,白为1,蓝为2

输入一组{0,1,2,1,2,0,2,0,1,2,0,1,1},返回{0,0,0,0,1,1,1,1,2,2,2,2}

#include <stdio.h>


void Sort(int *pData,int length){
    //头指针
    int *pstart = pData;
    //尾指针
    int *pend = pData + length - 1;
    //中指针
    int *pcurrent = pData;
    //临时变量
    int temp;
    
    while(pcurrent <= pend){
        //current指针指向元素为0
        //与start指针所指元素交换,current++,start++
        if(*pcurrent ==0){
            temp = *pstart;
            *pstart = *pcurrent;
            *pcurrent = temp;
            
            pstart ++;
            pcurrent++;
        }
        //current指针指向元素为1,不做任何交换,current++
        else if(*pcurrent ==1){
            pcurrent ++;
        }
        else{//current指针指向元素为2,与end指针所指元素交换,current不动,end--
            temp = *pcurrent;
            *pcurrent = *pend;
            *pend = temp;
            pend--;
        }
    }
}
int main() { int numbers[] = {0,1,2,1,2,0,2,0,1,2,0,1,1}; int i =0; Sort(numbers,12); for(;i<12;i++) printf("%d\n",numbers[i]); return 0; }

 

删除有序数组中的重复值

原文链接

如何去除有序数组的重复元素

我们知道对于数组来说,在尾部插入、删除元素是比较高效的,时间复杂度是 O(1),但是如果在中间或者开头插入、删除元素,就会涉及数据的搬移,时间复杂度为 O(N),效率较低。

所以对于一般处理数组的算法问题,我们要尽可能只对数组尾部的元素进行操作,以避免额外的时间复杂度。

这篇文章讲讲如何对一个有序数组去重,先看下题目:

显然,由于数组已经排序,所以重复的元素一定连在一起,找出它们并不难,但如果毎找到一个重复元素就立即删除它,就是在数组中间进行删除操作,整个时间复杂度是会达到 O(N^2)。而且题目要求我们原地修改,也就是说不能用辅助数组,空间复杂度得是 O(1)。

其实,对于数组相关的算法问题,有一个通用的技巧:要尽量避免在中间删除元素,那我就想先办法把这个元素换到最后去。这样的话,最终待删除的元素都拖在数组尾部,一个一个 pop 掉就行了,每次操作的时间复杂度也就降低到 O(1) 了。

按照这个思路呢,又可以衍生出解决类似需求的通用方式:双指针技巧。具体一点说,应该是快慢指针。

我们让慢指针 slow 走左后面,快指针 fast 走在前面探路,找到一个不重复的元素就告诉 slow 并让 slow 前进一步。这样当 fast 指针遍历完整个数组 nums 后,nums[0..slow] 就是不重复元素,之后的所有元素都是重复元素。

int removeDuplicates(int[] nums) {
    int n = nums.length;
    if (n == 0) return 0;
    int slow = 0, fast = 1;
    while (fast < n) {
        if (nums[fast] != nums[slow]) {
            slow++;
            // 维护 nums[0..slow] 无重复
            nums[slow] = nums[fast];
        }
        fast++;
    }
    // 长度为索引 + 1
    return slow + 1;
}

看下算法执行的过程:

再简单扩展一下,如果给你一个有序链表,如何去重呢?其实和数组是一模一样的,唯一的区别是把数组赋值操作变成操作指针而已:

ListNode deleteDuplicates(ListNode head) {
    if (head == null) return null;
    ListNode slow = head, fast = head.next;
    while (fast != null) {
        if (fast.val != slow.val) {
            // nums[slow] = nums[fast];
            slow.next = fast;
            // slow++;
            slow = slow.next;
        }
        // fast++
        fast = fast.next;
    }
    // 断开与后面重复元素的连接
    slow.next = null;
    return head;
}

 

posted @ 2020-03-10 17:21  -零  阅读(377)  评论(0编辑  收藏  举报