数据结构-个人课设

数据结构-个人课设

注意事项

  1. cin.get() 并不能排除所有空格,为了确保程序的鲁棒性,需要改成语句 cin.ignore(15,'\n') 表示最多忽视缓存区15个字符,或直到检测到换行符为止,换行符本身也会被忽略
  2. 为了检测非法数据,采用字符串流的形式完成输入,可能造成的问题是输入不健全导致链表等需要指针的数据结构出现内存错误,如访问空指针等
  3. 添加了关于非法数据的检测后可能导致OJ无法AC,稍微权衡一下不必考虑的面面俱到,限制条件尽量宽松,只要尽可能不让非法数据让程序出错即可。
  4. OJ上AC后我为了处理非法数据稍微修改了一下代码,重新交了一下十五题错了四个,其中有03和07是因为非法输入数据的处理导致的,13和15则是确实出错了,但其实不是思路问题,而是在实现思路的代码时出现了一些偏差,写的跟想的不一样导致一些很难检查出来的错误发生,比如在输入规模之前就申请特定规模的动态数组,或者比下标的大小写成了比下标对应数值的大小等等。

01-快速排序的应用

#include<iostream>
#include<cstdlib>
#include<string>
#include<sstream>
#include<string>
using namespace std;
const int MAXLISTSIZE_=1000;
template<class tp>
// 顺序表ADT
class SqList{
    int len;
    int listSize;
    tp * elem; 
  public:
    SqList(int ms=MAXLISTSIZE_){
        listSize=ms; //确定最大长度
        elem=new tp[listSize];  //创建数组
        if(!elem) exit(EXIT_FAILURE);
        len=0;
    } 
    SqList(tp *a,int le){
        len=le;
        elem=new tp[MAXLISTSIZE_];
        for(int i=0;i<len;i++){
            elem[i]=a[i];
        }
    }
    void destroySqlist(){
        delete []elem;
        len=0;
        listSize=0;
    }
    ~SqList(){
       destroySqlist();
    }
    bool compare(tp a,tp b){
        return a==b;
    }
    int locateElem(tp a,bool (*compare)(tp,tp)){
      int i;
      for(i=0;i<len;i++){
          if(compare(elem[i],a)){
              return i;
          }
      }
      return -1;
    }
    bool empty(){
        if(len==0) return 1;
        return 0;
    }
    bool full(){
        if(len==listSize) return 1;
        return 0;
    }
    void deleteElem(int pos,int lenth=1){
       
       if(len>0 && pos>=0 && pos+lenth<=len ){
           
           for(int i=pos;i<len;i++){
               elem[i]=elem[i+lenth];
           }
           len-=lenth;
       }
    }
    bool insertElem(int pos,tp a){
      if(pos>=0 && pos<=len && len<listSize){
        for(int i=len;i>pos;i--){
            elem[i]=elem[i-1];
        }
        elem[pos]=a;
        len++;
        return 1;
      } else {
          return 0;
      }
    }
    const int getLen() const {return len;}
    tp getElem(int i)const{
        return elem[i];
    }
    void printList(){
        for(int i=0;i<len;i++){
            if(i) cout<<" ";
            cout<<elem[i];
        }
        cout<<endl;
    }
    tp& operator[](int n){  //重载[]
      return elem[n];
    }
};

// 进行数字交换
void _swap(int &a,int &b){
  int k=a;
  a=b;
  b=k;
  return ;
}
// 快速排序
void quickSort(SqList<int> &S){
  int lf=0,rt=S.getLen()-1,c=0;
  while(lf<rt){
    if(S[lf]<0){
      lf++;
      continue;
    }
    if(S[rt]<0){
      if(!c) cout<<endl; //特殊情况
      c++;
      _swap(S[lf],S[rt]);
      lf++;
      S.printList();  //交换就输出
    }
    rt--;
  }
  return ;
}

int main(){
  SqList<int> Sql;
  int n,num;
  string s;
  cin>>n;
  if(n<=0){
    cout<<"err";
    return 0;
  }
  cin.ignore(15,'\n');
  getline(cin,s);
  int k=s.length();
  for(int i=0;i<k;i++){ //非法数据检测
    if(s[i]!='-' && s[i]!=' ' && (s[i]<'0' || s[i]>'9')){
      cout<<"err";
      return 0;
    }
  }

  stringstream ss(s);
  for(int i=0;i<n;i++){
    ss>>num;
    Sql.insertElem(i,num);
  }
  Sql.printList();
  quickSort(Sql);
  return 0;
}

检测非法输入的鲁棒性需要一定的考量

02-顺序表元素的快速删除

#include<iostream>
#include<cstdlib>
#include<string>
#include<sstream>
#include<string>
using namespace std;
const int MAXLISTSIZE_=1000;
template<class tp>
// 顺序表ADT
class SqList{
    int len;
    int listSize;
    tp * elem; 
  public:
    SqList(int ms=MAXLISTSIZE_){
        listSize=ms; //确定最大长度
        elem=new tp[listSize];  //创建数组
        if(!elem) exit(EXIT_FAILURE);
        len=0;
    } 
    SqList(tp *a,int le){
        len=le;
        elem=new tp[MAXLISTSIZE_];
        for(int i=0;i<len;i++){
            elem[i]=a[i];
        }
    }
    void destroySqlist(){
        delete []elem;
        len=0;
        listSize=0;
    }
    ~SqList(){
       destroySqlist();
    }
    bool compare(tp a,tp b){
        return a==b;
    }
    int locateElem(tp a,bool (*compare)(tp,tp)){
      int i;
      for(i=0;i<len;i++){
          if(compare(elem[i],a)){
              return i;
          }
      }
      return -1;
    }
    bool empty(){
        if(len==0) return 1;
        return 0;
    }
    bool full(){
        if(len==listSize) return 1;
        return 0;
    }
    void deleteElem(int pos,int lenth=1){
       
       if(len>0 && pos>=0 && pos+lenth<=len ){
           
           for(int i=pos;i<len;i++){
               elem[i]=elem[i+lenth];
           }
           len-=lenth;
       }
    }
    bool insertElem(int pos,tp a){
      if(pos>=0 && pos<=len && len<listSize){
        for(int i=len;i>pos;i--){
            elem[i]=elem[i-1];
        }
        elem[pos]=a;
        len++;
        return 1;
      } else {
          return 0;
      }
    }
    const int getLen() const {return len;}
    tp getElem(int i)const{
        return elem[i];
    }
    void printList(){
        for(int i=0;i<len;i++){
            if(i) cout<<" ";
            cout<<elem[i];
        }
    }
    tp& operator[](int n){  //重载[]
      return elem[n];
    }
};

// 进行数字交换
void _swap(int &a,int &b){
  int k=a;
  a=b;
  b=k;
  return ;
}
// 将item全部移至末尾,再统一删除
template<class ElemType>
void DeleteItem( SqList<ElemType> &A, ElemType item ){
  int lf=0,rt=A.getLen()-1,len=rt;
  while(lf<=rt){
    if(A[lf]!=item){
      lf++;
      continue;
    }
    if(A[rt]!=item){
      swap(A[lf],A[rt]);
      lf++;
      continue;
    }
    rt--;
  }
  // 删除lf开始的末尾item
  for(int i=len;i>=lf;i--){ //从后往前删除元素使得元素移动次数为0
    A.deleteElem(i);
  }
  if(A.getLen()==0) cout<<"empty";
  else{
    A.printList();
    cout<<" ";
  }
  return;
}

int main(){
  SqList<int> Sql;
  int n,num,item;
  string s;
  cin>>n;
  cin.ignore(15,'\n');
  getline(cin,s);
  cin>>item;
  int k=s.length();
  for(int i=0;i<k;i++){ //非法数据检测
    if(s[i]!='-' && s[i]!=' ' && (s[i]<'0' || s[i]>'9')){
      cout<<"err";
      return 0;
    }
  }

  stringstream ss(s);
  for(int i=0;i<n;i++){
    ss>>num;
    Sql.insertElem(i,num);
  }
  Sql.printList();
  cout<<" "<<endl<<endl;
  DeleteItem(Sql,item);
  return 0;
}

03-判断单链表的对称性

#include <iostream>
#include <string>
#include <sstream>
#include <stack>
using namespace std;
/* 单链表的结点定义 */
template <class ElemType>
struct LinkNode
{
    ElemType data;
    LinkNode<ElemType> *next;
    LinkNode(LinkNode<ElemType> *ptr = NULL) { next = ptr; }       //构造函数1,用于构造头结点
    LinkNode(const ElemType &item, LinkNode<ElemType> *ptr = NULL) //构造函数2,用于构造其他结点
    //函数参数表中的形参允许有默认值,但是带默认值的参数需要放后面
    {
        next = ptr;
        data = item;
    }
    ElemType getData() { return data; }                     //取得结点中的数据
    void SetLink(LinkNode<ElemType> *link) { next = link; } //修改结点的next域
    void SetData(ElemType value) { data = value; }          //修改结点的data值
};

template <class ElemType> //带头结点的单链表
class LinkList
{
private:
    LinkNode<ElemType> *head; // 头结点
    LinkNode<ElemType> *tail; // 尾结点
    int lenth;                // 链表长度(不计头结点)
public:
    LinkList() //无参数的构造函数
    {
        head = new LinkNode<ElemType>;
        lenth = 0;
        tail = head;
    }

    LinkList(const ElemType &item) //带参数的构造函数 -> 参数给头结点是否有点特殊?
    {
        head = new LinkNode<ElemType>(item);
        lenth = 0;
        tail = head;
    }

    LinkList(LinkList<ElemType> &List) //拷贝构造函数
    {
        head = new LinkNode<ElemType>;
        lenth = List.ListLength();
        LinkNode<ElemType> *p1 = List.GetHead()->next, *p2 = head, *p3;
        while (p1 != NULL)
        {
            *p3 = new LinkNode<ElemType>(*p1.data);
            *p2.SetLink(p3);
            p2 = p3;
            p1 = p1->next;
        }
        tail = p2;
    }

    ~LinkList() { ListDestroy(); } //析构函数

    LinkList<ElemType> &operator=(LinkList<ElemType> &List) //重载函数:赋值
    {
        ListClear();
        LinkNode<ElemType> *p1 = List.GetHead()->next, *p2 = head, *p3;
        while (p1 != NULL)
        {
            p3 = new LinkNode<ElemType>(p1->data);
            p2->SetLink(p3);
            p2 = p3;
            p1 = p1->next;
        }
        lenth = List.ListLength();
        tail = p2;
    }

    void ListDestroy() //销毁链表
    {
        LinkNode<ElemType> *p1 = head, *p2;
        while (p1 != NULL)
        {
            p2 = p1;
            p1 = p1->next;
            delete p2;
        }
    }

    void ListClear() //清空链表
    {
        lenth = 0;
        LinkNode<ElemType> *p1 = head->next, *p2;
        head->SetLink(NULL);
        while (p1 != NULL)
        {
            p2 = p1;
            p1 = p1->next;
            delete p2;
        }
        tail = head;
    }

    int ListLength() const { return lenth; } //返回链表的长度

    bool ListEmpty() const //判断链表是否为空表
    {
        if (lenth)
            return 1;
        else
            return 0;
    }

    bool InsFirst(ElemType e) //在首节点之前插入一个结点
    {
        lenth++;
        LinkNode<ElemType> *p2 = new LinkNode<ElemType>(e);
        p2->SetLink(head->next);
        head->SetLink(p2);
        return 1;
    }

    LinkNode<ElemType> *GetHead() const { return head; } //获取链表头结点

    void SetHead(LinkNode<ElemType> *p) { head = p; } //设置链表头结点
    
    void SetLenth(int l) { lenth=l; } //设置长度

    void SetTail(LinkNode<ElemType> *p) { tail = p; } //设置表尾节点

    LinkNode<ElemType> *GetTail() { return tail; } //获取链表尾结点

    bool ListInsert_prior(int pos, ElemType e) //在链表的第pos个位置之前插入e元素,可插在最末尾,不可覆盖头结点
    {
        if (pos <= 0 || pos > lenth + 1)
            return 0;
        lenth--;
        LinkNode<ElemType> *p1 = head;
        if (pos == lenth + 1)
        {
            LinkNode<ElemType> *p2 = new LinkNode<ElemType>(e);
            tail->SetLink(p2);
            tail = p2;
            return 1;
        }
        for (int i = 1; i <= pos - 1; i++)
        {
            p1 = p1->next;
            if (i == pos - 1)
            {
                LinkNode<ElemType> *p2 = new LinkNode<ElemType>(e);
                p2->SetLink(p1->next);
                p1->next = p2;
                return 1;
            }
        }
    }

    bool ListInsert_next(int pos, ElemType e) //在链表的第pos个位置之后插入e元素,可插在最末尾
    {
        if (pos < 0 || pos > lenth)
            return 0;

        lenth++;
        LinkNode<ElemType> *p1 = head;
        if (pos == lenth)
        {
            LinkNode<ElemType> *p2 = new LinkNode<ElemType>(e);
            tail->SetLink(p2);
            tail = p2;
            return 1;
        }
        for (int i = 1; i <= pos; i++)
        {
            p1 = p1->next;
            if (i == pos)
            {
                LinkNode<ElemType> *p2 = new LinkNode<ElemType>(e);
                p2->SetLink(p1->next);
                p1->next = p2;
                return 1;
            }
        }
    }
    //删除链表的首结点(注意不是头结点)
    bool DelFirst()
    {
        if (lenth == 0)
            return 0;
        if (lenth == 1)
            tail = head;  //维护尾指针
        lenth--;
        LinkNode<ElemType> *p1 = head->next;
        head->next = p1->next;
        delete p1;
        return 1;
    }
    //表头插入法动态生成链表 -> 和首节点插入元素有什么区别么= = -> 啊这个好像是数组!
    void CreateList_Head(int n, ElemType *A)
    {
        lenth += n;
        LinkNode<ElemType> *p1;
        for (int i = n - 1; i >= 0; i--)
        {
            p1 = new LinkNode<ElemType>(A[i]);
            p1->SetLink(head->next);
            head->SetLink(p1);
        }
    }
    //表尾插入法动态生成链表
    void CreateList_Tail(int n, ElemType *A)
    {
        lenth += n;
        LinkNode<ElemType> *p1;
        for (int i = 0; i < n; i++)
        {
            p1 = new LinkNode<ElemType>(A[i]);
            tail->SetLink(p1);
            tail = p1;
        }
    }
    //删除链表的第pos个位置的元素 -> 返回被删除的数据的话pos不合法怎么办呢?-> 返回空好了
    ElemType ListDelete(int pos)
    {
        ElemType tmp;
        if (pos <= 0 || pos > lenth)
            return tmp;
        lenth--;
        LinkNode<ElemType> *p1 = head, *p2;
        for (int i = 1; i <= pos; i++)
        {
            p2 = p1;
            p1 = p1->next;
            if (i == pos)
            {
                if(p1 == tail){  //维护尾指针
                    tail=p2;
                }
                p2->SetLink(p1->next);
                tmp = p1->data;
                delete p1;
                lenth--;        //维护长度
                return tmp;
            }
        }
    }
    LinkNode<ElemType> *GetElem(int pos) const //用e返回链表的第i个元素的地址 -> pos不合法返回什么呢? -> 空吧!
    {
        LinkNode<ElemType> *tmp;
        if (pos <= 0 || pos > lenth)
            return tmp;
        LinkNode<ElemType> *p1 = head;
        for (int i = 1; i <= pos; i++)
        {
            p1 = p1->next;
            if (i == pos)
                return p1;
        }
        return p1;
    }
    //按值查找,即定位。从链表的第一个结点开始,判断当前结点值是否等于e。
    //若是,则返回该结点的序号;否则继续后一个,直至表结束。找不到时返回0。
    int SearchElem(const ElemType &e) const
    {
        int c = 1;
        LinkNode<ElemType> *p1 = head->next;
        while (p1 != NULL)
        {
            if (p1->data == e)
                return c;
            c++;
            p1 = p1->next;
        }
        return 0;
    }
    //返回链表给定数据元素的前驱数据元素的值 -> 找不到或者为头结点就不改变咯
    bool PriorElem(const ElemType cur_e, ElemType &pri_e)
    {
        LinkNode<ElemType> *p1 = head->next->next, *p2 = head->next; //忽略头结点的可能
        while (p1 != NULL)
        {
            if (p1->data == cur_e)
            {
                pri_e = p2->data;
                return 1;
            }
        }
        return 0;
    }
    //遍历链表
    bool ListTraverse() const
    {
        if (lenth == 0)
            return 0;
        LinkNode<ElemType> *p1 = head;
        for (int i = 0;p1->next!=NULL; i++)  //不以lenth为标准
        {
            p1 = p1->next;
            if (i)
                cout << "->";
            cout << p1->data;
        }
        return 1;
    }

    void add(ElemType e)
    { //表尾添加节点
        LinkNode<ElemType> *p1 = new LinkNode<ElemType>(e);
        tail->SetLink(p1);
        tail = p1;
        lenth++;
    }
};
// 检测链表前n个结点是否中心对称
template<class ElemType>
bool Judge_Symmetry( LinkList<ElemType> &L, int num ){
  stack<ElemType> stac;
  if(num>L.ListLength()) num=L.ListLength(); //长度偏长
  int half=num/2;
  ElemType tmp;
  LinkNode<ElemType> *p=L.GetHead()->next;
  for(int i=0;i<=half-1;i++){  //前半段入栈
    stac.push(p->getData());
    p=p->next;
  }
  if(half*2!=num){  //跳过中间结点
    p=p->next;
  }
  for(int i=num-half;i<num;i++){
    tmp=stac.top();
    if(tmp!=p->getData()) return 0;
    p=p->next;
    stac.pop();
  }
  return 1;
}



int main(){
  int type;
  int n;
  cin>>type;
  
  switch(type){
    case 0:{
      LinkList<int> ll;
      cin>>n;
      if(n<=0){
        cout<<"err";
        return 0;
      }
      cin.ignore(15,'\n');
      string s;
      getline(cin,s);
      int len=s.length(),tmp;
      for(int i=0;i<len;i++){
        if(s[i]!='-' && s[i]!=' ' && (s[i]<'0' || s[i]>'9')){
          cout<<"err";
          return 0;
        }
      }
      stringstream ss(s);
      while(ss>>tmp){
        ll.add(tmp);
      }
      ll.ListTraverse();
      cout<<endl<<endl;
      if(Judge_Symmetry(ll,n)) cout<<"true";
      else cout<<"false";
      break;
    }
    case 1:{
      LinkList<double> ll;
      cin>>n;
      if(n<=0){
        cout<<"err";
        return 0;
      }
      cin.ignore(15,'\n');
      string s;
      getline(cin,s);
      int len=s.length();
      double tmp;
      for(int i=0;i<len;i++){
        if(s[i]!='-' && s[i]!=' ' && (s[i]<'0' || s[i]>'9') && s[i]!='.'){
          cout<<"err";
          return 0;
        }
      }
      stringstream ss(s);
      while(ss>>tmp){
        ll.add(tmp);
      }
      ll.ListTraverse();
      cout<<endl<<endl;
      if(Judge_Symmetry(ll,n)) cout<<"true";
      else cout<<"false";
      break;
    }
    case 2:{
      LinkList<char> ll;
      cin>>n;
      if(n<=0){
        cout<<"err";
        return 0;
      }
      cin.ignore(15,'\n');
      string s;
      getline(cin,s);
      int len=s.length();
      char tmp;
      stringstream ss(s);
      while(ss>>tmp){
        ll.add(tmp);
      }
      ll.ListTraverse();
      cout<<endl<<endl;
      if(Judge_Symmetry(ll,n)) cout<<"true";
      else cout<<"false";
      break;
    }
    case 3:{
      LinkList<string> ll;
      cin>>n;
      if(n<=0){
        cout<<"err";
        return 0;
      }
      cin.ignore(15,'\n');
      string s;
      getline(cin,s);
      int len=s.length();
      string tmp;
      stringstream ss(s);
      while(ss>>tmp){
        ll.add(tmp);
      }
      ll.ListTraverse();
      cout<<endl<<endl;
      if(Judge_Symmetry(ll,n)) cout<<"true";
      else cout<<"false";
      break;
    }
    default:{
      cout<<"err";
      return 0;
    }
  }

  return 0;
}

<!-1 处注意i赋初值不需要额外减一:9-4=5,范围是5-8,而下标4(顺序5)本身是被跳过的,因此直接从5开始即可;10-5=5,范围是5-9,直接从5开始即可;

04-搜索插入位置

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

// 二分查找下标或插入位置
int halfSearch(vector<int> vec,int tag){
  int lf=0,rt=vec.size()-1,m;
  while(lf<=rt){
    m=(lf+rt)/2;
    if(vec[m]<tag){
      lf=m+1;
    }
    if(vec[m]>tag){
      rt=m-1;
    }
    if(vec[m]==tag){
      return m;
    }
  }
  return lf;
}

int main(){
  vector<int> vec;
  string s;
  int n,aim,tmp;
  cin>>n;
  if(n<=0){
    cout<<"err";
    return 0;
  }
  cin.ignore(15,'\n');
  getline(cin,s);
  cin>>aim;
  int len=s.length();
  for(int i=0;i<len;i++){
    if(s[i]!='-' && s[i]!=' ' && (s[i]<'0' || s[i]>'9')){
      cout<<"err";
      return 0;
    }
  }
  stringstream ss(s);
  for(int i=0;i<n;i++){
    ss>>tmp;
    vec.push_back(tmp);
  }
  cout<<halfSearch(vec,aim);
  return 0;
}

05-有效的完全平方数

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

// 判断是否是完全平方数
bool isCompleteDouble(long long n){
  if(n==0 || n==1) return 1;
  long long m,lf=1,rt=n/2+1;
  while(lf<=rt){
    m=(lf+rt)/2;
    if(m*m<n){
      lf=m+1;
    }
    if(m*m>n){
      rt=m-1;
    }
    if(m*m==n){
      return 1;
    }
  }
  return 0;
}

int main(){
  string s;
  getline(cin,s);
  stringstream ss(s);
  int k=s.length();
  for(int i=0;i<k;i++){
    if(s[i]<'0' || s[i]>'9'){
      cout<<"err";
      return 0;
    }
  }
  int n;
  ss>>n;
  if(isCompleteDouble(n)) cout<<"true";
  else cout<<"false";
  return 0;
}

06-寻找比目标字母大的最小字母

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

// 二分查找大于目标字母的最小字母
char halfSearch(vector<char> vec,char tag){
  int lf=0,rt=vec.size()-1,m;
  while(lf<=rt){
    m=(lf+rt)/2;
    if(vec[m]<=tag){
      lf=m+1;
    }
    if(vec[m]>tag){
      rt=m-1;
    }
  }
  if(lf==vec.size()) return vec[0];
  else return vec[lf];
}

int main(){
  vector<char> vec;
  string s;
  char aim,tmp;
  int n;
  cin>>n;
  if(n<=0){
    cout<<"err";
    return 0;
  }
  cin.ignore(15,'\n');
  getline(cin,s);
  cin>>aim;
  int len=s.length();
  for(int i=0;i<len;i++){
    if(s[i]!=' ' && (s[i]<'a' || s[i]>'z')){
      cout<<"err";
      return 0;
    }
  }
  stringstream ss(s);
  for(int i=0;i<n;i++){
    ss>>tmp;
    vec.push_back(tmp);
  }
  cout<<halfSearch(vec,aim);
  return 0;
}

07-矩阵中战斗力最弱的K行

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

//搜寻最小的k行
void searchK(const vector<vector<int> > &V,vector<int> &v,const int k,const int n,const int m,vector<int> &flag){
  int cnt=0;
  for(int i=0;i<n;i++){
    for(int j=0;j<m;j++){
      if(V[j][i]==0){
        if(!flag[j]){
          cnt++;
          v.push_back(j);
          flag[j]=1;
          if(cnt==k) return;  //已找到k行
        }
      }
    }
  }

  for(int i=0;i<m;i++){ //未找到k行则逐行选择即可
    if(!flag[i]){
      cnt++;
      v.push_back(i);
      flag[i]=1;
      if(cnt==k) return ;
    }
  }
  return ;
}

int main(){
  string s;
  int n,m,k,tmp;
  cin>>m>>n;
  if(n<=0 || m<=0){
    cout<<"err";
    return 0;
  }
  vector<vector<int> > Vec(m,vector<int>(n));
  vector<int> min_vec,flag_vec(m,0);
  cin.ignore(15,'\n');
  for(int i=0;i<m;i++){
    getline(cin,s);
    int k=s.length();
/*    if(k!=2*n-1){ //检查0和1的数量是否合法
      cout<<"err";
      return 0;
    }	*/
    for(int j=0;j<k;j++){ //检查是否只有0和1
      if(s[j]!='1' && s[j]!='0' && s[j]!=' '){
        cout<<"err";
        return 0;
      }
    }
    stringstream ss(s);
    for(int j=0;j<n;j++){
      ss>>tmp;
      Vec[i][j]=tmp;
      if(j && Vec[i][j]>Vec[i][j-1]){ //检查1是否都在0的前面
        cout<<"err";
        return 0;
      }
    }
  }
  cin>>k;
  if(k>m){
    cout<<"err";
    return 0;
  }
  searchK(Vec,min_vec,k,n,m,flag_vec);
  for(int i=0;i<k;i++){
    cout<<min_vec[i]<<" ";
  }
  return 0;
}

起初想检测0和1的个数是否合法,但OJ上的测试数据不够严格,这样会AC不了,因此只能放弃,以上算法复杂度为O(n^2),但是看起来很明确,下面补一个时间复杂度为O(nlogn)的:

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

//搜寻最小的k行
void searchK(const vector<vector<int> > &V,const int k,const int n,const int m){
  int cnt=0;
  vector<int> oneNum(0),oneLoc(1,0);
  for(int i=0;i<m;i++){   // 二分查找1的个数
    int m,lf=0,rt=n-1;
    while(lf<=rt){
      m=(lf+rt)/2;
      if(V[i][m]==0){
        rt=m-1;
        continue;
      }
      if(V[i][m]==1){	//<!-1
        lf=m+1;
        continue;
      }
    }
    oneNum.push_back(lf);   //存1的个数
    oneLoc.push_back(i);  //存下标
    m=i+1;
    while(m>1){
      if(oneNum[oneLoc[m]]<oneNum[oneLoc[m/2]] || (oneNum[oneLoc[m]]==oneNum[oneLoc[m/2]] && oneLoc[m]<oneLoc[m/2])){
        oneLoc[m]=oneLoc[m]^oneLoc[m/2];
        oneLoc[m/2]=oneLoc[m]^oneLoc[m/2];
        oneLoc[m]=oneLoc[m]^oneLoc[m/2];
        m/=2;
      }else break;
    }
  }

  int len=m;

  for(int i=0;i<k;i++){
    cout<<oneLoc[1]<<" ";
    oneLoc[1]=oneLoc[len];
    oneLoc.pop_back();
    len--;
    int m=1;
    while(m<len){
      if(m*2+1<=len){
        if(oneNum[oneLoc[m*2+1]]<oneNum[oneLoc[m*2]] || (oneNum[oneLoc[m*2+1]]==oneNum[oneLoc[m*2]] && oneLoc[m*2+1]<oneLoc[m*2])){
          if(oneNum[oneLoc[m]]>oneNum[oneLoc[m*2+1]] || (oneNum[oneLoc[m]]==oneNum[oneLoc[m*2+1]] && oneLoc[m]>oneLoc[m*2+1])){
            oneLoc[m]=oneLoc[m]^oneLoc[m*2+1];
            oneLoc[m*2+1]=oneLoc[m]^oneLoc[m*2+1];
            oneLoc[m]=oneLoc[m]^oneLoc[m*2+1];
            m=m*2+1;
            continue;
          } else break;
        } else {
          if(oneNum[oneLoc[m]]>oneNum[oneLoc[m*2]] || (oneNum[oneLoc[m]]==oneNum[oneLoc[m*2]] && oneLoc[m]>oneLoc[m*2])){
            oneLoc[m]=oneLoc[m]^oneLoc[m*2];
            oneLoc[m*2]=oneLoc[m]^oneLoc[m*2];
            oneLoc[m]=oneLoc[m]^oneLoc[m*2];
            m=m*2;
            continue;
          } else break;
        }
      } else {
        if(m*2<=len){
          if(oneNum[oneLoc[m]]>oneNum[oneLoc[m*2]] || (oneNum[oneLoc[m]]==oneNum[oneLoc[m*2]] && oneLoc[m]>oneLoc[m*2])){
            oneLoc[m]=oneLoc[m]^oneLoc[m*2];
            oneLoc[m*2]=oneLoc[m]^oneLoc[m*2];
            oneLoc[m]=oneLoc[m]^oneLoc[m*2];
            m=m*2;
          }
        }
        break;
      }
    }
  }
  return ;
}

int main(){
  string s;
  int n,m,k,tmp;
  cin>>m>>n;
  if(n<=0 || m<=0){
    cout<<"err";
    return 0;
  }
  vector<vector<int> > Vec(m,vector<int>(n));
  cin.ignore(15,'\n');
  for(int i=0;i<m;i++){
    getline(cin,s);
    int k=s.length();
    for(int j=0;j<k;j++){ //检查是否只有0和1
      if(s[j]!='1' && s[j]!='0' && s[j]!=' '){
        cout<<"err";
        return 0;
      }
    }
    stringstream ss(s);
    for(int j=0;j<n;j++){
      ss>>tmp;
      Vec[i][j]=tmp;
      if(j && Vec[i][j]>Vec[i][j-1]){ //检查1是否都在0的前面
        cout<<"err";
        return 0;
      }
    }
  }
  cin>>k;
  if(k>m){
    cout<<"err";
    return 0;
  }
  searchK(Vec,k,n,m);
  return 0;
}

先用二分查找每行1的个数(这是因为1总是出现在0的前面),然后存入数组并利用小根堆进行插入排序,二分与小根堆插入都是O(logn)的复杂度(堆插入排序是普通插入排序的完美替代,虽然代码看起来挺复杂的就是了),因此这一部分的时间复杂度为O(nlogn),下面输出前k个弱的行,由于一个数组不能同时存下标和1的个数,因此分成两个数组(相当于索引排序),然后依次去除小根堆的根节点,并进行小根堆删除操作(每次将序列最后一个数放到根节点上,然后逐层向下比较进行堆调整),由于堆运算的特殊性,数组从1开始便于计算,因此 oneLoc 数组从下标1开始。结果最后RE了一个测试数据是因为 <!-1 处"i"下标越界,因为循环处写了 i<n ,而实际有m行,n可能大于m,我以为是出现了死循环一直检查下面的堆操作。

08-找到和最大的长度为k的子序列

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
using namespace std;
vector<int> Vec,_index;

bool cmp(int a,int b){  //索引排序
  return Vec[a]>Vec[b];
}

bool cmp1(int a,int b){   //前k个索引从小到大排序
  return a<b;
}

//搜索长度为k的最大子序列
void isMaxK(vector<int> &Vec,vector<int> &index,int k,int n){
  for(int i=0;i<n;i++){
    index[i]=i;
  }
  sort(index.begin(),index.end(),cmp);

  vector<int> maxSum(k);
  for(int i=0;i<k;i++){
    maxSum[i]=Vec[index[i]];
  }
  sort(index.begin(),index.begin()+k,cmp1);
  for(int i=0;i<k;i++){
    cout<<Vec[index[i]]<<" ";
  }
  return ;
}

int main(){
  int n,tmp,k;
  cin>>n;
  if(n<=0){
    cout<<"err";
    return 0;
  }
  Vec.resize(n);
  _index.resize(n);

  string s;
  cin.ignore(15,'\n');
  getline(cin,s);

  int len=s.length();
  for(int i=0;i<len;i++){   //合法性检查
    if(s[i]!=' ' && s[i]!='-' && (s[i]<'0' || s[i]>'9')){
      cout<<"err";
      return 0;
    }
  }
  
  cin>>k;
  if(k>n){
    cout<<"err";
    return 0;
  }

  stringstream ss(s);
  for(int i=0;i<n;i++){
    ss>>tmp;
    Vec[i]=tmp;
  }
  isMaxK(Vec,_index,k,n);


  return 0;
}

09-无人机方阵

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
using namespace std;

int main(){
  int n,m,tmp;
  cin>>n>>m;
  if(n<=0 || m<=0){
    cout<<"err";
    return 0;
  }

  int bul[10000+10],cnt=0;
  for(int i=0;i<10000;i++){
    bul[i]=0;
  }
  string s;
  cin.ignore(15,'\n');
  for(int i=0;i<n;i++){ //source
    getline(cin,s);
    int len=s.length();
    for(int j=0;j<len;j++){
      if(s[j]!=' ' && s[j]!='-' && (s[j]<'0' || s[j]>'9')){
        cout<<"err";
        return 0;
      }
    }
    stringstream ss(s);
    for(int j=0;j<m;j++){
      ss>>tmp;
      bul[tmp-1]++;
    }
  }

  for(int i=0;i<n;i++){ //target
    getline(cin,s);
    int len=s.length();
    for(int j=0;j<len;j++){
      if(s[j]!=' ' && s[j]!='-' && (s[j]<'0' || s[j]>'9')){
        cout<<"err";
        return 0;
      }
    }
    stringstream ss(s);
    for(int j=0;j<m;j++){
      ss>>tmp;
      bul[tmp-1]--;
    }
  }

  for(int i=0;i<10000;i++){
    if(bul[i]<0) cnt+=-bul[i];
  }

  cout<<cnt;
  return 0;
}

10-完成一半题目

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
using namespace std;
vector<int> bul(1000+10);

// 从大到小索引快速排序
bool cmp(int a,int b){
  return bul[a]>bul[b];
}

// 最少的题目种类
int minType(vector<int> &bul,int n){
  vector<int> index(1000+10);
  for(int i=0;i<1000;i++){
    index[i]=i;
  }
  sort(index.begin(),index.begin()+1000,cmp);
  n/=2;
  int cnt=0,i=0;
  while(n>0){
    n-=bul[index[i]];
    cnt++;
    i++;
  }
  return cnt;
}

int main(){
  int n,tmp;
  cin>>n;
  if(n<=0 || n%2!=0){
    cout<<"err";
    return 0;
  }

  for(int i=0;i<1000;i++){
    bul[i]=0;
  }
  string s;

  cin.ignore(15,'\n');
  getline(cin,s);
  int len=s.length();
  for(int j=0;j<len;j++){
    if(s[j]!=' ' && (s[j]<'0' || s[j]>'9')){
      cout<<"err";
      return 0;
    }
  }
  stringstream ss(s);
  for(int j=0;j<n;j++){
    ss>>tmp;
    bul[tmp-1]++;
  }

  cout<<minType(bul,n);
  return 0;
}

11-错误的集合

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
using namespace std;

// 找出重复的数字
void whichNum(vector<int> &Vec){
  int len=Vec.size();
  for(int i=1;i<len;i++){
    if(Vec[i]!=Vec[i-1]+1){
      for(int j=i+1;j<len;j++){
        if(j==i+1){
          if(Vec[j]!=Vec[j-2]+2){
            cout<<"Multiple Error";
            return ;
          }
          continue;
        }
        if(Vec[j]!=Vec[j-1]+1){
          cout<<"Multiple Error";
          return ;
        }
      }
      cout<<Vec[i]<<" "<<Vec[i-1]+1<<" ";
      return ;
    }
  }
  cout<<"All Correct";
  return ;
}

int main(){
  int n,tmp;
  cin>>n;
  if(n<=1){
    cout<<"err";
    return 0;
  }
  vector<int> Vec(n);

  string s;
  cin.ignore(15,'\n');
  getline(cin,s);
  int len=s.length();
  for(int j=0;j<len;j++){
    if(s[j]!=' ' && (s[j]<'0' || s[j]>'9')){
      cout<<"err";
      return 0;
    }
  }
  stringstream ss(s);
  for(int j=0;j<n;j++){
    ss>>tmp;
    Vec[j]=tmp;
  }

  whichNum(Vec);
  return 0;
}

12-至少是其他数字两倍的最大数

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
using namespace std;

// 最大数是否是其他数字的两倍及以上
int isMaxDouble(vector<int> &Vec){
  int flag=1,len=Vec.size();\
  int max=0;
  for(int i=1;i<len;i++){
    if(Vec[i]>Vec[max]){
      if(Vec[i]>=Vec[max]*2){
        max=i;
        flag=1;
      } else {
        max=i;
        flag=0;
      }
      continue;
    }
    if(Vec[i]*2>Vec[max]){
      flag=0;
    }
  }
  if(flag) return max;
  else return -1;
}

int main(){
  int n,tmp;
  cin>>n;
  if(n<=0){
    cout<<"err";
    return 0;
  }
  vector<int> Vec(n);

  string s;
  cin.ignore(15,'\n');
  getline(cin,s);
  int len=s.length();
  for(int j=0;j<len;j++){
    if(s[j]!=' ' && s[j]!='-' && (s[j]<'0' || s[j]>'9')){
      cout<<"err";
      return 0;
    }
  }
  stringstream ss(s);
  for(int j=0;j<n;j++){
    ss>>tmp;
    Vec[j]=tmp;
  }

  cout<<isMaxDouble(Vec);
  return 0;
}

13-K次取反后最大化的数组和

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
using namespace std;

// k次取反后可能的最大和
int possibleMaxSum(vector<int> &Bul,int k){
  int sum=0,last;
  for(int i=0;i<=200;i++){
    if(Bul[i]){
      if(i-100<0){
        if(Bul[i]<k){
          sum+=Bul[i]*-(i-100);
          k-=Bul[i];
          last=i;
          continue;
        }else{
          sum+=k*-(i-100)+(Bul[i]-k)*(i-100);
          for(int j=i+1;j<=200;j++){
            sum+=Bul[j]*(j-100);
          }
          return sum;
        }
      } else {
        if(k%2){
          if(i+last-200>0){ //考虑绝对值的大小	<!-1
            sum+=(last-100)*2;      //原来是加上,现在撤销加上再减去一次相当于减去两次
            sum+=(i-100)*Bul[i];
            for(int j=i+1;j<=200;j++){
              sum+=Bul[j]*(j-100);
            }
          }else{
            sum-=i-100;
            sum+=(i-100)*(Bul[i]-1);
            for(int j=i+1;j<=200;j++){
              sum+=Bul[j]*(j-100);
            }
          }
          return sum;
        } else {
          for(int j=i;j<=200;j++){
            sum+=Bul[j]*(j-100);
          }
          return sum;
        }
      }
    }
  }

  if(k%2){
    sum+=(last-100)*2;
    return sum;
  } else {
    return sum;
  }

}

int main(){
  int n,tmp,k;
  cin>>n;
  if(n<=0){
    cout<<"err";
    return 0;
  }
  vector<int> Bul(201,0);

  string s;
  cin.ignore(15,'\n');
  getline(cin,s);
  int len=s.length();
  for(int j=0;j<len;j++){
    if(s[j]!=' ' && s[j]!='-' && (s[j]<'0' || s[j]>'9')){
      cout<<"err";
      return 0;
    }
  }
  cin>>k;
  stringstream ss(s);
  for(int j=0;j<n;j++){
    ss>>tmp;
    Bul[tmp+100]++;
  }

  cout<<possibleMaxSum(Bul,k);
  return 0;
}

<!-1 考虑绝对值时错把式子写成了 Bul[i]+Bul[last]>0 导致WA了一组测试案例始终找不出来

14-检查整数及其两倍数是否存在

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
using namespace std;

// 二分搜索某个元素是否存在
bool halfSearch(vector<int> &Vec,int tag){
  int lf=0,rt=Vec.size()-1;
  while(lf<=rt){
    int m=(lf+rt)/2;
    if(Vec[m]==tag){
      return 1;
    }
    if(Vec[m]<tag){
      lf=m+1;
    } else rt=m-1;
  }
  return 0;
}

// 从小到大快速排序
bool cmp(int a,int b){
  return a<b;
}

// 检查数组中是否存在一个数是另一个数两倍的情况
bool isDoubleExist(vector<int> &Vec){
  sort(Vec.begin(),Vec.end(),cmp);
  int len=Vec.size();
  for(int i=0;i<len;i++){
    if(halfSearch(Vec,Vec[i]*2)) return 1;
    if(Vec[i]*2>Vec[len-1]) return 0;
  }
   return 0;
}

int main(){
  int n,tmp,k;
  cin>>n;
  if(n<=0){
    cout<<"err";
    return 0;
  }
  vector<int> Vec(n);

  string s;
  cin.ignore(15,'\n');
  getline(cin,s);
  int len=s.length();
  for(int j=0;j<len;j++){
    if(s[j]!=' ' && s[j]!='-' && (s[j]<'0' || s[j]>'9')){
      cout<<"err";
      return 0;
    }
  }
  stringstream ss(s);
  for(int j=0;j<n;j++){
    ss>>tmp;
    Vec[j]=tmp;
  }

  if(isDoubleExist(Vec)) cout<<"true";
  else cout<<"false";
  return 0;
}

以上时间复杂度为O(nlogn),提供一个通过哈希实现的复杂度为O(n)的算法:

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
using namespace std;


int main(){
  int n,tmp,k;
  cin>>n;
  if(n<=0){
    cout<<"err";
    return 0;
  }
  vector<int> Vec(n),Bul(2010,0);

  string s;
  cin.ignore(15,'\n');
  getline(cin,s);
  int len=s.length();
  for(int j=0;j<len;j++){
    if(s[j]!=' ' && s[j]!='-' && (s[j]<'0' || s[j]>'9')){
      cout<<"err";
      return 0;
    }
  }
  stringstream ss(s);
  for(int j=0;j<n;j++){
    ss>>tmp;
    Vec[j]=tmp;
    Bul[tmp+1000]++;
  }
  for(int i=500;i<=1500;i++){
    if(Bul[i]){
      if(Bul[(i-1000)*2+1000] && i!=1000){
        cout<<"true";
        return 0;
      }
    }
  }
  cout<<"false";
  return 0;
}

由于数据范围是给定的 [-1000,1000] ,因此直接建个数组装进去就行,然后从前往后扫描一遍两倍是否同时存在即可,为了下标不越界同时减少遍历次数,i 从500开始也就是-500开始,1500结束也就是500结束。WA了一个是因为 i-1000 写成了 i-500 ,不知道说什么好

15-数据流的第K大数值

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
using namespace std;

// 从大到小排序
bool cmp(int a,int b){
  return a>b;
}

class KthLargest{
  int Kth; //k的值
  vector<int> Num;
  public:
  KthLargest(int k,int* nums,int n):Kth(k){
    Num.resize(n);
    for(int i=0;i<n;i++){
      Num[i]=nums[i];
    }
    sort(Num.begin(),Num.end(),cmp);
  }
  // 增添元素
  void add(int n){
    Num.push_back(n);
    int i=0;
    while(n<Num[i]){
      i++;
    }
    for(int j=Num.size()-1;j>i;j--){
      Num[j]=Num[j-1];
    }
    Num[i]=n;
  }
  void printK(){
    cout<<Num[Kth-1];
  }
};

int main(){
 int k,n,tmp;
 cin>>k>>n;
 int *nums=new int[n];
 if(k<=0 || k>n || n<=0){
  cout<<"err";
  return 0;
 }
 string s;
 cin.ignore(15,'\n');
 getline(cin,s);
 int len=s.length();
 for(int i=0;i<len;i++){
  if(s[i]!='-' && s[i]!=' ' && (s[i]<'0' || s[i]>'9')){
    cout<<"err";
    return 0;
  }
 }
 stringstream ss(s);
 for(int i=0;i<n;i++){
  ss>>tmp;
  nums[i]=tmp;
 }
  KthLargest KL(k,nums,n);
  while(getline(cin,s)){
    int len=s.length();
    if(s.substr(0,4)!="add "){
      cout<<"err"<<endl;
      continue;
    }
    int ff=0;
    for(int i=4;i<len;i++){
      if(s[i]!='-' && s[i]!=' ' && (s[i]<'0' || s[i]>'9')){
        cout<<"err"<<endl;
        ff=1;
        break;
      }
    }
    if(ff) continue;
    stringstream ss(s.substr(4,len-4));
    ss>>tmp;
    KL.add(tmp);
    KL.printK();
    cout<<endl;
  }
   delete[] nums;
 return 0;

}

这题有点搞的地方在于我后来润色代码的时候把语句 int *nums=new int[n]; 放到了输入语句前,导致AC上一直RE,我以为是下标越界可是始终找不到问题出在哪,思索调试了半天当看到 unknown signal 的报错时才确定一定是下标越界,然后给把语句改成了 int *nums=new int[n+10]; ,测试案例在本地过了,但是OJ上依然有问题,然后才发现申请动态数组时n的值还未输入,所以申请的动态数组太小了,调整了一下位置后成功AC。

以上代码每次插入数据的时间复杂度为O(n),下面提供一个时间复杂度为O(logk)的的算法:

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <queue>
using namespace std;

class KthLargest{
  int K; //k的值
  priority_queue<int,vector<int>,greater<int> > Kth;
  public:
  KthLargest(int k,int* nums,int n):K(k){
    for(int i=0;i<n;i++){
      Kth.push(nums[i]);
    }
    for(int i=0;i<n-K;i++){
      Kth.pop();
    }
  }
  // 增添元素
  void add(int n){
    Kth.push(n);
    Kth.pop();
  }
  void printK(){
    cout<<Kth.top();
  }
};

int main(){
 int k,n,tmp;
 cin>>k>>n;
 int *nums=new int[n];
 if(k<=0 || k>n || n<=0){
  cout<<"err";
  return 0;
 }
 string s;
 cin.ignore(15,'\n');
 getline(cin,s);
 int len=s.length();
 for(int i=0;i<len;i++){
  if(s[i]!='-' && s[i]!=' ' && (s[i]<'0' || s[i]>'9')){
    cout<<"err";
    return 0;
  }
 }
 stringstream ss(s);
 for(int i=0;i<n;i++){
  ss>>tmp;
  nums[i]=tmp;
 }
  KthLargest KL(k,nums,n);
  while(getline(cin,s)){
    int len=s.length();
    if(s.substr(0,4)!="add "){
      cout<<"err"<<endl;
      continue;
    }
    int ff=0;
    for(int i=4;i<len;i++){
      if(s[i]!='-' && s[i]!=' ' && (s[i]<'0' || s[i]>'9')){
        cout<<"err"<<endl;
        ff=1;
        break;
      }
    }
    if(ff) continue;
    stringstream ss(s.substr(4,len-4));
    ss>>tmp;
    KL.add(tmp);
    KL.printK();
    cout<<endl;
  }
   delete[] nums;
 return 0;

}

使用了优先队列(自动排序),greater<int> 表示从小到大排,初始化时输入n个数据,再删除n-k个数据,保持优先队列规模为k,而队列头上的数据就是第K大的数据,时间复杂度为O(nlogn)(其实还能变成O(logk),只要规模超过k了一边插入一边删除就行),每次插入数据时直接推入优先队列再从中删除一个,队列头上的数据就还是第K大的数据。

posted @   Festu  阅读(98)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示