算法基础(二)(共有30道例题)

六、数据结构

(一)数组

  1. 定义:数组是存放在连续内存空间上的相同类型数据的集合。数组可以方便的通过下标索引的方式获取到下标下对应的数据。
    注意:
    (1)数组下标都是从0开始的。
    (2)数组内存空间的地址是连续的。正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
    (3)使用C++的话,要注意vector 和 array的区别,vector的底层实现是array,严格来讲vector是容器,不是数组。
    (4)二维数组在内存的空间地址是连续的么?
    答:不同编程语言的内存管理是不一样的,以C++为例,在C++中二维数组是连续分布的
1.数组之二分查找
例题1:二分查找

这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素。这是最简单的二分查找的题目。
题目 难度:简单

class Solution {
   
public:
    int search(vector<int>& nums, int target) 
    {
   
        int left = 0;
        int right = nums.size() - 1;
        while (left <= right)//截至条件相当于是区间的大小 < 1
        //假如left==right,那么left==middle==right
        {
   
            int middle = left + ((right - left) / 2);
            if(nums[middle]>target)//说明target在middle左边
            {
   
                right=middle-1;
            }
            else if(nums[middle]<target)//说明target在middle右边
            {
   
                left=middle+1;
            }
            else return middle;//nums[middle]==target
        }
        return -1;
    }
};
例题2:数的范围

在这里插入图片描述
本质是:边界有一半区间满足条件,一半不满足。我们可以找到这两个区间的边界。相当于找绿色区域的左边界,找红色区域的右边界。
mid是中间的下标,x是要查找的数。

  1. 找左边界
    如果mid满足绿色区域的条件(q[mid]>=x),那么从右侧逼近,right=mid,如果mid不满足绿色区域的条件,即mid在红色区域,那就从左侧逼近,left=mid+1;
  2. 找右边界
    如果mid满足红色区域的条件(q[mid]<=x),那么从左侧逼近,left=mid,如果mid不满足红色区域的条件,即mid在绿色区域,那就从右侧逼近,right=mid-1;

题目

#include <iostream>
using namespace std;

const int N = 100010;
int n, m;
int q[N];

int main()
{
   
    cin>>n>>m;  
    for(int i=0;i<n;i++) cin>>q[i];    
    while(m--)
    {
   
        int x;
        cin>>x;//要查找的数
    
        int left=0,right=n-1;                
        while(left<right)
        {
   
            int mid = (left + right) >> 1;
            if(q[mid]>=x) right=mid;//向左逼近,找左边界  x...mid...
            else left=mid+1;//mid...x...
        }
        
        if(q[left]!=x) cout<<"-1 -1"<<endl;
        else
        {
   
            cout<<left<<" ";
                        
            int left = 0, right = n - 1;      
            
            while(left<right)
            {
   
                int mid=(left + right +1) >> 1;
                if (q[mid] <= x) left = mid;//向右逼近,找右边界
                else right = mid - 1;
            }
            cout<<right<<endl;
        }
    }        
    return 0;
}
例题3:在排序数组中查找元素的第一个和最后一个位置

题目 难度:中等

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

        if(n==0) return{
   -1,-1};
    
        int left=0,right=n-1;
        
        while(left<right)
        {
   
            int mid = (left + right) >> 1;
            if(nums[mid]>=target) right=mid;//向左逼近,找左边界  x...mid...
            else left=mid+1;//mid...x...
        }
        
        if(nums[left]!=target) return {
   -1,-1};
        else
        {
   
            res[0]=left;
            
            int left = 0, right = n - 1;
        
            while(left<right)
            {
   
                int mid=(left + right +1) >> 1;
                if (nums[mid] <= target) left = mid;//向右逼近,找右边界
                else right = mid - 1;
            }
            res[1]=left;
        }
        return res;
    }      
};
2.数组之移除元素
例题4:移除元素

题目 难度:中等
数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
方法一:暴力

class Solution {
   
public:
    int removeElement(vector<int>& nums, int val) 
    {
   
        int len=nums.size();
        for(int i=0;i<len;i++)
        {
   
            if(nums[i]==val) //找到要删除的元素了
            {
   
                for (int j = i + 1; j < len; j++) 
                {
   
                    nums[j - 1] = nums[j];
                }
                i--;
                len--;
            }
        }
        return len;
    }
};

方法二:双指针
定义快慢指针
快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组。
慢指针:指向更新 新数组下标的位置。

class Solution {
   
public:
    int removeElement(vector<int>& nums, int val) 
    {
   
        int slow_index = 0;
        //快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
        //慢指针:指向更新 新数组下标的位置
        for(int fast_index=0;fast_index<nums.size();fast_index++)
        {
   
            if (val != nums[fast_index]) //如果不是要移除的元素
            {
   
                nums[slow_index] = nums[fast_index];
                slow_index++;
            }
        }
        return slow_index;
    }
};

(二)链表

1.链表之静态链表(用数组模拟单链表)
例题5:单链表

题目
注意:题目中第 k个插入的数并不是指当前链表的第 k 个数。例如操作过程中一共插入了 n个数,则按照插入的时间顺序,这 n 个数依次为:第 1 个插入的数,第 2 个插入的数,…第 n 个插入的数。

#include<iostream>
using namespace std;

const int N=1e6+10;
// head存储链表头(表示头节点的下标),e[i]存储节点i的值,ne[i]存储节点i的next指针,idx表示当前用到了哪个节点(相当于指针)
int head, e[N], ne[N], idx;

// 初始化:链表是空的:head->空,空节点的下标用-1来表示
void init()
{
   
    head = -1;
    idx = 0;
}
//在表头插入一个数a
void add_to_head(int a)
{
   
    e[idx] = a;
    ne[idx] = head;//(1)插入的节点的next指针指向了原本head指向数据
    head=idx;//(2)原本的head指向了插入元素a;
    idx++;
}

//题目中第k个插入的数并不是指当前链表的第k个数。
//例如操作过程中一共插入了n个数,则按照插入的时间顺序,这n个数依次为:第1个插入的数,第2个插入的数,…第 n个插入的数。

//将数据x插入到下标是k的节点后面
void add_to_k(int k,int x)
{
   
    e[idx]=x;
    ne[idx]=ne[k];
    ne[k]=idx;
    idx++;
}
//将第k个的节点后面的一个节点删掉
void remove(int k)
{
   
    ne[k]=ne[ne[k]];
}

int main()
{
   
    int m;//操作次数
    cin>>m;    
    init();//初始化    
    while(m--)
    {
   
        char s;
        int k;
        int x;
    
        cin>>s;
        if(s=='H')
        {
   
            cin>>x;
            add_to_head(x);
        }
        else if(s=='I')
        {
   
            cin>>k>>x;
            add_to_k(k-1,x);
        }
        else 
        {
   
            cin>>k;
            if(!k) head=ne[head];//当k为0时,表示删除头结点。
            //举例:1,2,3,4。k=0时,删除1即可,即head指向2,2的下标为ne[head];   
            remove(k-1);
        }
    }    
    for(int i=head;i!=-1;i=ne[i])
    {
   
        cout<<e[i]<<' ';
    }
    cout<<endl; 
    return 0;
}
2.链表之静态链表(用数组模拟双链表)
例题6:双链表

题目

#include<iostream>
using namespace std;
#include<string>

const int N=1e6+10;

//e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点
int e[N], l[N], r[N], idx;

//struct Node{
   
    //int e,l,r;
//}nodes[N];

// 初始化
void init()
{
   
    //0是左端点(head),1是右端点(tail)
    r[0] = 1, l[1] = 0;
    idx = 2;//因为0和1已经被占用了
}
//表示在第 k 个插入的数右侧插入一个数x。
void add(int k,int x)
{
   
    e[idx]=x;
    r[idx]=r[k];
    l[idx]=k;
    
    l[r[k]]=idx;//r[k]是第k个插入节点的右边节点的idx
    r[k]=idx;
    
    idx++;
}
//在第 k 个插入的数左侧插入一个数x:其实就是在第l[k]个数右边插入一个数x
//add(l[k],x)

//表示将第 k 个插入的数删除,第 k 个数左边的数的下标l[k],第 k 个数右边的数的下标r[k]
void remove(int k)
{
   
    r[l[k]]=r[k];
    l[r[k]]=l[k];
    
    //nodes[nodes[k].l].r=nodes[k].r;
}

int main()
{
   
    int m;//操作次数
    cin>>m;
    init();
    while(m--)
    {
   
        string s;
        cin>>s;
        int k;
        int x;
        if(s=="L")//表示在链表的最左端插入数x,即在head的右边插入x
        {
   
            cin>>x;
            add(0,x);
        }
        else if(s=="R")//表示在链表的最右端插入数x,即在tail的左边插入x
        {
   
            cin>>x;
            add(l[1],x);
        }
        else if(s=="D")
        {
   
            cin>>k;
            remove(k+1);
        }
        else if(s=="IR")
        {
   
            cin>>k>>x;
            add(k+1,x);
        }
        else
        {
   
            cin>>k>>x;
            add(l[k+1],x);
        }
    }
    for(int i=r[0];i!=1;i=r[i]) cout<<e[i]<<' ';
    cout<<endl;
    return 0;
}
3.链表之删除元素
例题7:移除链表元素:给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

题目 难度:简单
在这道题里头结点(题目中也给出了定义:ListNode* head)的val是有正常数字的,不是摆设,所以我们可以设一个虚拟头结点放在原来头结点前面。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
   
public:
    ListNode* removeElements(ListNode* head, int val) 
    {
   
        ListNode* dummy_head=new ListNode(0);//设置虚拟头结点
        dummy_head->next=head;//设置虚拟头节点;放在head前面
        ListNode *cur=dummy_head;
        while(cur->next!=nullptr)
        {
   
            if(cur->next->val==val)
            {
   
                ListNode *node_delete=cur->next;
                ListNode *tmp=cur->next->next;
                //cur是要删除结点的上一个结点的指针,cur->next->next是要删除结点的下一个结点指针
                cur->next=tmp;
                delete node_delete;
            }
            
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值