算法刷题-数组

JZ4 二维数组中的查找

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

解决思路:

正常:双循环,挨个查找;【但是这种线性查找方法效率极低】

进阶:根据题面给出的条件(左到右递增,上到下递增),我们可以采取从右上角(或左下角)开始比较这样可以做到一次排除一行或者一列。

比如

1 2 3
2 3 4
4 5 6  //查找5
//右上角:是所在行的最大值,所在列的最小值
//3比5小 ==> 列++
//4比5小 ==> 列++
//6比5大 ==> 行--

在这个过程中要注意避免数组下标越界,所以循环的条件比较重要

class Solution {
public:
    bool Find(int target, vector > array) {
        int row = array.size();//行
        int col = array[0].size();//列
        //array[i][j]
        int i = 0, j = col - 1;//用右上角的值来比较
        while(i < row && j >= 0)
        {
            //比右上角的值大,行++
            if(target > array[i][j]) i++;
            //比右上角的值小,列--
            else if(target < array[i][j]) j--;
            else if(target == array[i][j]) return true;
        }
        //走到这说明找不到
        return false;
    }
};

JZ11 旋转数组的最小数字

**题目描述:**有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。

解题思路

正常:1、遍历数组,找最小值【线性查找方法效率太低】;2、双指针,同时比较当前值和下一个值的大小。【时间复杂度与1一样,没有性能提升】

进阶:借鉴二分查找的思想,一个数组经过旋转后,得到的结果可以看作两个新的非降序数组,我们要做的就是找到两个新数组的分割位置,通过不断缩小[left, right]最终确定最小值!

left、right、mid三个下标,

  1 2 3 4 5//非降序数组
//情况1
  2    3    4    5    1
left       mid       right
此时a[left]<=a[mid],说明前半段是非降序数组;a[right]

归纳:当a[left]<=a[mid]时,则要搜索后半段

//情况2
  5    1    2    3    4//旋转后
left       mid       right
此时a[left]>a[mid],说明前半段顺序是不对的;a[right]>a[mid],说明后半段是非降序数组,那么对前半段再次进行查找
  5    1    2    3    4//旋转后
left  mid   right
此时a[left]>a[mid],说明前半段顺序是不对的;a[right]>a[mid],说明后半段是非降序数组,那么对前半段再次进行查找
  5       1    2    3    4//旋转后
left/mid right
此时left==mid,说明right所指向的就是最小值

归纳:当a[left]>a[mid]时,则要搜索前半段

有一层隐含关系:当(left==mid) > (left+1right) ==> right指向的就是最小值。

非递减排序可能会出现a[left]==a[mid]==a[right]这种情况,我们就无法判定最小值在mid左侧还是右侧,就需要线性遍历了。

  1    1    1    0    1//旋转后
left       mid       right

总的循环条件是rotateArray[left] >= rotateArray[right](能进入循环的肯定是旋转数组!),在进入循环之前mid=0,这样可以保证遇到1 2 2 2(未旋转的数组)这种情况,仍可以判断出来

class Solution {
public:
    int minNumberInRotateArray(vector rotateArray) {
        if(rotateArray.empty()) return 0;
        int left = 0;
        int right = rotateArray.size() - 1;
        int mid = 0;//如果这个数组是1 2 2 2没有旋转的数组,就是mid

        while(rotateArray[left] >= rotateArray[right])//保证该数组是旋转过的数组
        {
            if(right - left == 1)//两个下标相邻,右索引就是最小值
            {
                mid = right;
                break;
            }
            mid = (right + left) / 2;
            //如果无法判断大小,就线性遍历搜索最小值
            if(rotateArray[left] == rotateArray[mid] && rotateArray[right] == rotateArray[mid])
            {
                int min = rotateArray[left];
                for(int i = left + 1; i <= right; i++)
                {
                    if(rotateArray[i] < min)
                    {
                        min = rotateArray[i];                        
                    }
                }
                //遍历结束,返回最小值
                return min;
            }
            
            if(rotateArray[mid] >= rotateArray[left]) left = mid;//搜索后半段
            else right = mid;//搜索前半段
        }
        return rotateArray[mid];
    }
};

调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。如1 2 3 4 5 6

划重点:相对位置不变,得到1 3 5 2 4 6

解题思路

正常:1、改变相对位置。头尾双指针,头指针遇到奇数就++,遇到偶数停下来交换;尾指针遇到偶数就–,遇到奇数就停下来交换。2、不改变相对位置。new两个数组,遍历原数组,一个数组保存奇数,一个数组保存偶数,遍历结束,再把偶数数组的数据添加到奇数数组的后面。

进阶:借鉴插入排序的思想。从头开始遍历,遇到奇数就从当前位置开始从后向前覆盖式地挪动,直到上一个奇数处,留出了一个空位给当前奇数插入。覆盖式挪动,刚好把当前奇数覆盖掉,避免了数据冗余

class Solution {
public:
    void reOrderArray(vector &array) {
        int k = 0;
        for(int i = 0; i < array.size(); i++)
        {
            if(array[i] & 1)//奇数
            {
                int tmp = array[i];//保存奇数,后续进行覆盖挪动
                int j = i;//从当前下标开始挪动,所以当前下标位置的数据会被覆盖
                while(j > k)
                {
                    array[j] = array[j-1];
                    j--;
                }
                array[k++] = tmp;
            }
        }
    }
};

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

题目描述:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

解题思路

思路一:定义map,使用<数字,次数>的映射关系,最后统计每个字符出现的次数。

思路二:排序,出现次数最多的数字,一定在中间位置。然后检测中间出现的数字出现的次数是否符合要求。

思路三:由于目标数据超过数组长度的一半这个前提条件,那么我们同时去掉两个不同的数字,到最后剩下的一个数就是该数字。如果剩下两个,那么这两个也是一样的,就是结果,在其基础上把最后剩下的一个数字或者两个,用原数组遍历,将数组遍历一遍统计一下数字出现次数进行最终判断。

采用消去法来解决,一个数据与另一个数据不同,则times=0表示已消去

class Solution {
public:
    int MoreThanHalfNum_Solution(vector numbers) {
        if(numbers.size() == 0) return 0;

        int number = numbers[0];
        int times = 1;
        for(int i = 1; i < numbers.size(); i++)
        {
            if(times == 0)
            {
                number = numbers[i];
                times = 1;
                continue;
            }
            if(numbers[i] == number)
                times++;
            else times--;//两个数不同,则抵消
        }
        
        //后续还要再确认一下是否是这个数据
        int count = 0;
        for(int i = 0; i < numbers.size(); i++)
        {
            if(numbers[i] == number) count++;
        }

        return count > numbers.size()/2 ? number : 0;
    }
};

你可能感兴趣的:(算法刷题,算法,leetcode,数据结构)