数据结构与算法5-查找

数据结构与算法-查找

往期内容
1-链表
2-栈与队列
3-树与图
4-哈希表
5-查找
6-排序
7-贪心
8-递归与分治
9-动态规划



一、常用的查找方法

1.1入门查找

  • 常规
int Sequential_Search(int *a,int n,int key)
{
    for(int i=0;i<n;i++)
    {
        if(a[i]==key)
            return i;
    }
    //否则返回0:错误
    return 0;
}
  • 哨兵
//哨兵
//优化后的顺序查找
int Sequential_Search_Optimize(int *a,int n,int key)
{
    a[0]=key;//哨兵
    int i=n;
    while(a[i]!=a[0])
    {
        i--;
    }
    return i;//返回0表示查找失败
}

1.2 二分查找

//1.基本的二分查找
int binarySearch(int *arr,int n,int targer)
{
    int left=0,right=n-1;//搜索空间全为闭

    while(left<=right)//相当于两个区间上全部为闭区间
    {
        int mid=left+(right-left)/2;//可有效防止整型相加溢出风险
        if(arr[mid]==targer)//退出条件
            return mid;
        else if(arr[mid]>targer)//区间向左减小条件
            right=mid-1;
        else if (arr[mid]<targer)//区间向右减小条件
        {
            left=mid+1;
        }
        
    }
    return -1;//计算错误
}
//2.递归写法
//2.二分查找递归代码实现
bool search(int *arr,int begin,int end,int target)
{
    if(begin>end)
        return false;
    int mid=(begin+end)/2;

    if(target==arr[mid])
        return true;
    else if(target<arr[mid])
    {
        return search(arr,begin,mid-1,target);
    }
    else //if(target>arr[mid])
    {
        return search(arr,mid+1,end,target);
    }
}
bool binary_search(int *arr,int n,int target)
{
    return search(arr,0,n-1,target);
}

1.3二叉排序树

1.3.1性质

  • 若她的左子树不为空,左子树上的所有值均小于它结点的值
  • 若她的右子树不为空,右子树上的所有值均大于它结点的值
  • 它的左右子树也分别为二叉排序树

1.3.2 结构

二叉排序树的实质,还是一个二叉树,因此它的结构依旧是树的结构

struct TreeNode{
    int val;//值
    TreeNode* left;
    TreeNode* right;
};

1.3.3 查找

二叉排序树的查找和二分查找的代码结构类似,带查找值和根结点比较,大于根结点访问右子树,小于根结点访问左子树

//1.查找
//root:树的根节点,key:需要查找的元素,parent:指向当前root的双亲结点,初始化为null,posNode:查找成功的结点
bool SearchBST(TreeNode* root,int key,TreeNode* parent,TreeNode** posNode)
{
    //递归:第一步找到边界条件
    if(root==NULL){
        *posNode=parent;//未找到时,当前结点的指针指向最终的叶子结点
        return false;
    }
    else if(root->val==key)//找到了该元素
    {
        *posNode=root;//找到时,当前结点的指针指向该结点
        return true;
    }
    else if(key<root->val)//左边查找
    {
        return SearchBST(root->left,key,root,posNode);
    }
    else
    {
        return SearchBST(root->right,key,root,posNode);
    }
}

1.3.4 插入

//2.插入操作
bool InsertBST(TreeNode** root,int key)
{
    TreeNode* p;//遍历查找时使用

    TreeNode* s;//创建新结点时使用


    if(!SearchBST(*root,key,NULL,&p))//如果当前结点元素不在树中,插入
    {

        s=(TreeNode*)malloc(sizeof(TreeNode));
        s->val=key;

        s->left=NULL;//不要忘记了
        s->right=NULL;

        if(p==NULL)
        {
            *root=s;
        }
        else if(p->val>key) //左边插入
        {
            p->left=s;
        }
        else//右结点插入
        {
            p->right=s;
        }

        return true;


    }
    else{
        return false;
    }

}

1.3.5 删除

请神容易,送神难,删除需要考虑多种情况

  • 要删除结点只有左子树或者只有右子树(子承父业),将左子树或者右子树整体移动到待删除结点的位置即可。
  • 如果既有左子树,又有右子树
//3.二叉树的删除操作
//如何删除单个结点-》》》》》》情况一,只有左孩子或者右孩子,情况二,有两个孩子
bool Delete(TreeNode **root)
{
    //定义两个指向结点的指s针
    TreeNode* s;
    TreeNode* q;//释放结点内存的指针

    if((*root)->left==NULL)
    {
        q=*root;
        *root=(*root)->right;//指向下一个左孩子
        free(q);

    }
    else if((*root)->right==NULL)
    {
        q=*root;
        *root=(*root)->left;
        free(q);
    }
    else{
        //使用左子树中最大的元素替代该节点
        s=*root;

        q=(*root)->left;//转到左边结点


        while(q->right)//找到右结点为空的结点(最大的结点q),s为q的前向结点
        {
            s=q;//q指向待结点的前驱结点
            q=q->right;
        }
        //搬移数据
        (*root)->val=q->val;
        //结点联结,删除结点
        if(*root!=s)//如果root结点的左子结点无右结点:
        {
            //链接左节点
            s->right=q->left;
        }
        else{           //如果root结点的左子结点有右结点
            s->left=q->left;
        }

        free(q);//回收q的指向,当前的结点
    }

    return true;
}



//如何删除某个结点
bool DeleteBST(TreeNode** root,int key)
{
    if(*root==NULL) return false;
    else
    {
        if((*root)->val==key)
            return Delete(root);
        else if(key<(*root)->val)
            return DeleteBST(&((*root)->left),key);
        else
            return DeleteBST(&((*root)->right),key);
    }
}

1.4 AVL平衡二叉树

平衡二叉树是一种二叉排序树,其中每个结点的左子树和右子树的高度差的绝对值至多等于1 注意:(0,-1,+1)

1.4.1 平衡二叉树的结构

多增加了一个变量用于存放平衡因子

//定义一个树的结构,但是要增加一个bf用作:结点的平衡因子
struct BiTNode{
    int data;
    int bf;//计算平衡因子
    BiTNode *left,*right;
};

1.4.2 四种保证平衡的方法

在这里插入图片描述
因此

  • 单右旋
//BF>1:左高右低,右旋
void R_Rotate(BiTNode **root)//二级指针,需要改变树的结构
{
    BiTNode* L;//左子树结点指针
    L=(*root)->left;

    (*root)->left=L->right;
    L->right=(*root);
    *root=L;
}
  • 单左旋
//BF<1:左低右高,左旋
void L_Rotate(BiTNode **root)
{
    BiTNode* R;//左子树结点指针
    R=(*root)->right;

    (*root)->right=R->left;
    R->left=(*root);
    *root=R;
}
  • 左平衡
//1.左平衡操作
void LeftBalance(BiTNode** root)
{
    BiTNode *L,*Lr;//左平衡的两个操作:右单旋,先左后右旋转
    L=(*root)->left;
    switch(L->bf)
    {
        case LH:
            (*root)->bf=L->bf=EH;
            R_Rotate(root);
            break;
        case RH:
            Lr=L->right;
            switch(Lr->bf)
            {
                case LH:
                    (*root)->bf=RH;
                    L->bf=EH;
                    break;
                case EH:
                    (*root)->bf=EH;
                    L->bf=EH;
                    break;
                case RH:
                    (*root)->bf=EH;
                    L->bf=LH;
                    break;
            }
            Lr->bf=EH;
            L_Rotate(&(*root)->left);
            R_Rotate(root);
    }
}
  • 右平衡
//2.右边平衡操作
void RightBalance(BiTNode** root)
{
    BiTNode *R,*Rl;//左平衡的两个操作:右单旋,先左后右旋转
    
    R=(*root)->right;
    switch(R->bf)
    {
        case RH:
            R->bf=(*root)->bf=EH;
            L_Rotate(root);
            break;
        case LH:
            Rl=R->left;
            switch(Rl->bf)
            {
                case LH:
                    (*root)->bf=EH;
                    R->bf=RH;
                    break;
                case EH:
                    (*root)->bf=EH;
                    R->bf=EH;
                    break;
                case RH:
                    (*root)->bf=LH;
                    R->bf=EH;
                    break;
            }
            Rl->bf=EH;
            R_Rotate(&(*root)->right);
            L_Rotate(root);
    }
}

1.4.3 算法

bool InsetAVL(BiTNode** root,int key,bool *taller)//参数三:判断树是否长高
{
    if(!*root)//如果树为空
    {
        *root=(BiTNode*)malloc(sizeof(BiTNode));
        (*root)->data=key;
        (*root)->left=(*root)->right=NULL;
        (*root)->bf=EH;
        *taller=true;
    }
    else{
        if((*root)->data==key)  //如果当前结点值等于key,则不需要插入
        {
            *taller=false;
            return false;
        }
        else if((*root)->data>key)//在左边插入
        {
            //左区间递归
            if(!InsetAVL(&(*root)->left,key,taller))
                return false;
            if(taller)//树长高了?
            {
                //因为从左边插入,只需要检测左边的bf即可
                switch((*root)->bf)
                {
                    case LH:
                        LeftBalance(root);
                        (*root)->bf=LH;//保持不变,可以不写
                        *taller=false;
                        break;
                    case EH:
                        (*root)->bf=LH;
                        *taller=true;
                        break;
                    case RH:
                        (*root)->bf=EH;
                        *taller=false;
                        break;
                }
            }
        }
        else//在当前节点的右边插入
        {
            //右区间递归
            if(!InsetAVL(&(*root)->right,key,taller))
                return false;
            if(taller)//树长高了?
            {
                //因为从右边边插入,只需要检测root的bf即可
                switch((*root)->bf)
                {
                    case LH:
                        (*root)->bf=EH;
                        *taller=false;
                        break;
                    case EH:
                        (*root)->bf=RH;
                        *taller=true;
                        break;
                    case RH:
                        RightBalance(root);
                        (*root)->bf=RH;//保持不变
                        *taller=false;
                        break;
                }
            }
        }

    }
    return true;
}

1.5 散列表

存储位置=f(关键字)
处理哈希冲突:开放定址,找下一个位置。
再散列函数法:准备多个散列函数
链地址法:


二、Leetcode刷题

二分查找和搜索

2.1 搜索插入位置

Leetcode-35 搜索插入位置
思路:用一个标志位index记录,找到还是插入,二分查找:情况一:找到该元素,万事大吉。情况 二:未找到该元素,如果 target < nums[mid],且target > nums[mid - 1]和target > nums[mid],且target < nums[mid + 1];两种情况分开讨论

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int begin=0;
        int end=nums.size()-1;
        int index=-1;//返回的下标

        while(index==-1)//还未找到正确的位置
        {
            int mid=begin+(end-begin)/2;
            if(nums[mid]==target)
                index=mid;
            else if(target<nums[mid])
            {
                if(mid==0 || target>nums[mid-1])
                    index=mid;
                end=mid-1;
            }
            else if(target>nums[mid])
            {
                if(mid==nums.size()-1 || target<nums[mid+1])
                {
                    index=mid+1;
                }
                begin=mid+1;
            }

        
        }
        return index;
    }
};

2.2 在排序数组中查找元素的第一个和最后一个位置

Leetcode-34 在排序数组中查找元素的第一个和最后一个位置
思路1:暴力从前往后遍历

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        bool flag=false;
        int begin=-1;
        int end=-1;
        for(int i=0;i<nums.size();i++)
        {
            if(nums[i]==target && !flag)
            {
                begin=i;   
                end =i;
                flag=true;
            }
            if(flag)
            {
                if(nums[i]!=target)
                {
                    break;
                }
                end=i;
            }

        }
        vector<int> res;
        res.push_back(begin);
        res.push_back(end);
        return res;
    }
};

思路2:二分查找,当找到目标元素的时候,依次向前和向后遍历,直到找到区间

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> result={-1,-1};

        int begin=0;
        int end=nums.size()-1;

        while(begin<=end)
        {
            int mid=begin+(end-begin)/2;

            if(target==nums[mid])
            {
                int left=mid;
                int right=mid;

                while(left >0 && target==nums[left-1])
                {
                    left=left-1;
                }

                while(right <(nums.size()-1) && target==nums[right+1])
                {
                    right=right+1;
                }

                result[0]=left;
                result[1]=right;

                break;
            }
            else if(target>nums[mid])
            {
                begin=mid+1;
            }
            else if(target<nums[mid])
            {
                end=mid-1;
            }

        }
        return result;
    }
};

思路3:控制变量,找到左端点和右端点
在这里插入图片描述

2.3 搜索旋转排序数组

Leetcode-33 搜索旋转排序数组

思路:二分查找,分情况讨论

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(nums.size()<0) return -1;
        if (nums.size() == 1) {
            return nums[0] == target ? 0 : -1;
        }
        int left = 0, right = nums.size() - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] == target) return mid;
            if (nums[0] <= nums[mid]) {
                if (nums[0] <= target && target < nums[mid]) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            } else {
                if (nums[mid] < target && target <= nums[nums.size() - 1]) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
        }
        return -1;
    }
};

2.4 岛屿数量

思路1:深度优先遍历

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        vector<vector<int>> mark(grid.size(),vector<int>(grid[0].size(),0));
        int count=0;

        for(int i=0;i<grid.size();i++)
        {
            for(int j=0;j<grid[0].size();j++)
            {
                if(mark[i][j]==0 && grid[i][j]=='1')//注意条件
                {
                    count++;
                    DFS_search(grid,mark,i,j);
                }
                

            }
        }
        return count;
    }


    void DFS_search(vector<vector<char>>& grid,vector<vector<int>>& mark,int x,int y)
    {
        mark[x][y]=1;
        static const int dx[4]={0,0,-1,1};
        static const int dy[4]={1,-1,0,0};

        for(int i=0;i<4;i++)
        {
            int new_x=x+dx[i];
            int new_y=y+dy[i];

            if(new_x>=0 && new_y>=0 && new_x<mark.size() && new_y<mark[new_x].size())
            {
                if(mark[new_x][new_y]==0 && grid[new_x][new_y]=='1')//没有访问且是连通的(注意条件)
                {
                    DFS_search(grid,mark,new_x,new_y);
                }
            }
        }
    }
};

思路2:广度优先遍历

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        vector<vector<int>> mark(grid.size(),vector<int>(grid[0].size(),0));
        int count=0;
        for(int i=0;i<grid.size();i++)
        {
            for(int j=0;j<grid[0].size();j++)
            {
                if(mark[i][j]==0 && grid[i][j]=='1')
                {
                    count++;
                    BFS_search(grid,mark,i,j);
                }
            }
        }
        return count;
    }

    void BFS_search(vector<vector<char>>& grid,vector<vector<int>>& mark,int x,int y)
    {
        static const int dx[]={0,0,1,-1};
        static const int dy[]={1,-1,0,0};
        queue<pair<int,int>> Q;
        Q.push(make_pair(x,y));

        mark[x][y]=1;

        while(!Q.empty())
        {
            x=Q.front().first;
            y=Q.front().second;
            Q.pop();

            for(int i=0;i<4;i++)
            {
                int new_x=x+dx[i];
                int new_y=y+dy[i];
                if(new_x>=0 && new_y>=0 && new_x<grid.size() && new_y<grid[new_x].size())
                {
                    if(mark[new_x][new_y]==0 && grid[new_x][new_y]=='1')
                    {
                        Q.push(make_pair(new_x,new_y));  
                        mark[new_x][new_y]=1;      
                    }
                }
            }
        }
    }
};

2.5 单词接龙

Leetcode-127 单词接龙
思路:
在这里插入图片描述

class Solution {
public:
    //判断两个字符是否可以转换
    bool isConnect(string &word1,string &word2){
        int count=0;
        for(int i=0;i<word1.size();i++)
        {
            if(word1[i]!=word2[i])
                count++;
        }
        return count==1;
    }

    //创建图
    void construct_graph(string &beginWord,  vector<string>& wordList,
    map<string,vector<string>> &graph){
        wordList.push_back(beginWord);

        for(int i=0;i<wordList.size();i++)
        {
            graph[wordList[i]]=vector<string>();
        }

        for(int i=0;i<wordList.size();i++)
        {
            for(int j=i+1;j<wordList.size();j++)
            {
                if(isConnect(wordList[i],wordList[j]))
                {
                    graph[wordList[i]].push_back(wordList[j]);
                    graph[wordList[j]].push_back(wordList[i]);
                }
            }
        }
    }
    //宽度优先搜索
    int BFS_search(string &beginWord,string &endWord,
    map<string,vector<string>> &graph,set<string> &visit){
        queue<pair<string,int>> Q;
        Q.push(make_pair(beginWord,1));
        visit.insert(beginWord);

        while(!Q.empty())
        {
            string str=Q.front().first;
            int len=Q.front().second;
            Q.pop();



            for(auto temp:graph[str])
            {
                if(visit.find(temp)==visit.end())
                {
                    Q.push(make_pair(temp,len+1));
                    visit.insert(temp);

                    if(temp==endWord)
                        return len+1;
                }
            }

        }
        return 0;


    }

    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        map<string,vector<string>> graph;
        set<string> visit;
        construct_graph(beginWord,wordList,graph);

        return BFS_search(beginWord,endWord,graph,visit);
    }
};

2.6 火柴拼正方形

Leetcode-473 火柴拼正方形
思路:递归加剪枝

class Solution {
public:
    bool makesquare(vector<int>& matchsticks) {
        //参考求子集的代码
        if(matchsticks.size()<4)
            return false;
        int sum=accumulate(matchsticks.begin(),matchsticks.end(),0);
        if(sum%4!=0)
            return false;
        
        int side[4]={0};//四条边
        int target=sum/4;
        sort(matchsticks.begin(),matchsticks.end(),[](int a,int b){return a>b;});
        return generate(0,matchsticks,target,side);
    }

    //回溯思想
    bool generate(int i,vector<int>& matchsticks,int &target,int side[])
    {
        if(i>=matchsticks.size())
            return (side[0]==target && side[1]==target && side[2]==target && side[3]==target);
        
        for(int j=0;j<4;j++)//在四个桶中分别尝试
        {
            if((side[j]+matchsticks[i])>target)
            {
                continue;
            }
            side[j]+=matchsticks[i];
            if(generate(i+1,matchsticks,target,side))
            {
                return true;
            }
            side[j]-=matchsticks[i];
        }
        return false;

    }


};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值