Leetcode刷题笔记(C++)

  • 链表的操作
    • #include<iostream>
      using namespace std;
      typedef int elemType;
      
      template <typename elemType>
      class linkList {
      
      private:
          struct Node                //结点类型
          {
              elemType data;         //结点数据域
              Node* next;            //结点指针域
              Node(const elemType value, Node* p = NULL) {
                  data = value;
                  next = p;
              }
              Node(Node* p = NULL) {
                  next = p;
              }
          };
          Node* head;             //链表头结点
          Node* tail;             //链表尾结点
          int curLength;          //单链表当前长度,
          Node* getPosition(int i) const;  //返回指向位序为i的结点的指针
      
      public:
          linkList();
          ~linkList();
      
          void clear();                 //清空单链表,使之成为空表
          bool empty() const {          //带有头结点的单链表,判空
              return head->next == NULL; 
          } 
          int size() const {            //返回单链表的当前实际长度
              return curLength;
          }
          void insert(int i, const elemType& value);  //位序i处插入值为value的结点,表长加1
          void remove(int i);           //移除位序为i处的结点,表长减1
          int search(const elemType& value) const;             //查找值为value的结点第一次出现的位序
          int prior(const elemType& value)  const;             //查找值为value的结点的前驱的位序
          void traverse() const;        //遍历单链表
          void headCreate();            //头插法建立单链表
          void tailCreate();            //尾插法建立单链表
          void inverse();               //逆置单链表
          linkList<elemType>* Union(linkList<elemType>* lb);  //合并单链表
      };
      
      
      /*
      查找位序为i的结点的内存地址(思想):
          1.首先检查要查找位序的合法性,若非法返回NULL,i==0表示查找首元结点,i==-1表示查找头结点
          2.设置工作指针,指向头结点,设置计数器,进行计数,知道p指向位序为i的结点
      */
      template<class elemType>
      typename linkList<elemType>::Node* linkList<elemType>::getPosition(int i) const
      {
          if (i <-1 || i > curLength-1) {         //合法检查范围为[-1...curLength-1]
              return NULL;
          }
          Node* p = head;           //工作指针指向头结点
          int count = 0;
          while (count <= i) {
              p = p->next;
              count++;
          }
          return p;                 //返回指向位序为i结点的指针
      }
      
      /*
      单链表初始化:
          创建一个带有头结点的空链表,即:
              申请一个新的节点作为头结点,不设置节点数据域,只设置其指针域为空即可
      */
      template<typename elemType>
      linkList<elemType>::linkList()
      {
          head = tail =new Node();  //创建带有头结点的单链表
          curLength = 0;
      }
      
      template<typename elemType>
      linkList<elemType>::~linkList()
      {
          clear();              //清空单链表
          delete head;          //释放头结点
      }
      
      /*
      清空单链表:
          将工作指针从头结点移动到表尾,边移动指针,边删除结点。
          不修改头指针head的指向,而是引入一个工作指针完成单链表的遍历
      */
      template<typename elemType>
      void linkList<elemType>::clear()
      {
          Node* p, * tmp;   //声明p为工作指针,指向首元结点
          p = head->next;   //引入工作指针,是为了防止随意修改头指针
          while (p != NULL) {
              tmp = p;         
              p = p->next;    //指针后移
              delete tmp;     //释放结点
          }
          head->next = NULL;  //最后头结点的指针域置空
          tail = head;        //头尾指针均指向头结点
          curLength = 0;
      
      }
      
      /*
      插入结点(思想):
          位序为i处插入值为value的结点q:
          1. 先找到位序为i的结点的前驱结点p
          2. q的后继指向p原来的后继,修改p的后继为q
          3. 合法的插入范围[0,curLength],其中0表示插入点为首元结点处,curLength为表尾。
          4. 插入算法主要是移动指针查找结点,所以时间复杂度为O(n).
      */
      template<typename elemType>
      void linkList<elemType>::insert(int i, const elemType& value) 
      {
          Node* p, * q;                //q为要插入的新节点,p为q的前驱结点
          if (i<-1 || i>curLength) {
              throw out_of_range("Out of Range");   //插入位置非法,抛出异常
          }
          p = getPosition(i - 1);     //定位到i结点的前驱
          q = new Node(value,p->next);    //申请新节点q,数据域为value,指针域为p结点的下一个结点
          p->next = q;                    //q结点插入到p结点后边
          if (p == tail) tail = q;        //若插入点在表尾,则q结点为新的表尾
          curLength++;                    //表长+1
      
      }
      
      /*
      删除指定位序的结点(思想):
          1. 检验删除范围是否合法,合法范围为[0,curLength-1],0表示删除首元结点,curLength-1表示删除表尾结点
          2. 找到位序为i-1 的结点,修改指针的指向关系,时间复杂度为O(n)
      */
      template<typename elemType>
      void linkList<elemType>::remove(int i)
      {
          if (i<0 || i>curLength - 1) {
              throw out_of_range("Out of Range");
          }
          Node* pre, * p;
          pre= getPosition(i - 1);      //待删除结点的前驱结点
          p = pre->next;                       //待删除结点
          if (p == tail) {                     //要删除的结点为表尾结点
              tail = pre;                      //修改表尾指针
              p->next = NULL;                     //表尾指针置NULL
              delete p;                         //删除当前结点
          }
          else {                                 //修改指针并删除结点p
              pre->next = p->next;
              delete p;
          }
          curLength--;
      }
      
      /*
      查找值为value的元素的位序(思想):
          1. 设置计数器count,从链表开头遍历进行判断,
          2. 查找成功,返回count,否则直到单链表结束(p==NULL),返回-1.
          3. 时间复杂度O(n).
      */
      template<typename elemType>
      int linkList<elemType>::search(const elemType& value) const
      {
          int count = 0;                      //位序0 为首元结点
          Node* p = head->next;
          while (p!=NULL && p->data != value) {
              p = p->next;
              count++;
          }
          if (p == NULL) return -1;         //查找失败,返回-1,此-1并非头结点
          else return count;            //查找成功,返回结点位序
      
      }
      
      /*
      查找值为value的结点 的前驱位序(思想):
          1.设置计数器,两个指针p和pre,分别指向当前访问的结点和它的前驱。
              1. 若p==NULL,则查找失败,返回-1
              2. 若找到值为value的结点,且该结点是首元结点,则无前驱,返回-1
              3. 若找到值为value的结点,且该结点不是首元结点,则返回前驱的位序
      */
      template<typename elemType>
      int linkList<elemType>::prior(const elemType& value) const
      {
          Node* p = head->next;     //p为工作指针,指向首元结点
          Node* pre = nullptr;      //pre指向p的前驱
          int count = -1;           //-1表示没有前驱结点
      
          while (p != nullptr && p->data != value) {
              pre = p;              //前驱指针后移一位
              p = p->next;          //当前指针后移,指向下一个待处理结点
              count++;
          }
          if (p == NULL) return -1; //查找失败,返回-1
          else return count;        //成功,返回位序
          return 0;
      }
      
      /*
      遍历单链表(思想):
          从头到尾遍历链表中的每个结点,一次输出
          时间复杂度为O(n)
      */
      template<typename elemType>
      void linkList<elemType>::traverse() const
      {
          Node* tmp = head->next;    //工作指针指向首元结点
          while (tmp != NULL) {
              cout << tmp->data << " ";  
              tmp = tmp->next;       //向后移动
          }
          cout << endl;
      }
      
      /*
      头插法建立单链表(思想):
          头插法即指在链表的头部插入结点建立单链表:每次插入将新增结点置于头结点之后,首元结点之前
          因为是在单链表的头部插入,所以读入数据的顺序会是相反的(与线性表中的逻辑结构相反)
          时间复杂度为O(n)
      */
      template<typename elemType>
      void linkList<elemType>::headCreate()
      {
          Node* p;
          elemType value, flag;
          cout << "Input elements,ends with:" << endl;
          cin >> flag;               //输入结束标志
          while (cin >> value, value != flag) {
              //创建新的节点p->data=value; p->next=head->next;
              p = new Node(value, head->next);
              head->next = p;                    //节点p插入在头结点的后面
              if (head == tail) tail == p;    //原链表为空,新节点p称为尾结点
              curLength++;
          }
      }
      
      /*
      尾插入建立单链表(思想):
          在单链表的尾部插入结点建立单链表,
          初始时,创建一个带有头结点的空链表,头指针和尾指针均指向头结点
          按顺序表中的元素顺序读入元素并申请结点,将新节点插入tail指针的后边,修改tail指向为新节点
      */
      template<typename elemType>
      void linkList<elemType>::tailCreate()
      {
          Node* p;
          elemType value, flag;
          cout << "Input elements,ended with: " << endl;
          cin >> flag;
          while (cin >> value, value != flag) {
              p = new Node(value, NULL);
              tail->next = p;             //节点p插入在尾结点之后
              tail = p;                    //移动尾指针,使其指向新的结点
              curLength++;
          }
      }
      
      /*
      逆置单链表:
          利用头插法建立的单链表,对于元素的顺序与读入的顺序时相反的。
          1. 用工作指针p依次访问单链表中的每个结点,每访问一个节点,将它插入在头结点之后,
          2. 向后移动工作指针,知道所有结点全部重新插入到单链表之中,即完成了单链表的逆置。
          3. 时间复杂度为O(n)
      */
      template<typename elemType>
      void linkList<elemType>::inverse()
      {
          Node* p, * tmp;
          p = head->next;            //工作指针,指向首元结点
          head->next = nullptr;      //头结点指针域为空,构成空链表
          if (p) tail = p;           //逆置后,原来的首元结点变成尾结点
          while (p) {
              tmp = p->next;         //暂存p的后继
              p->next = head->next;  //
              head->next = p;        //结点p插入在头结点后边
              p = tmp;               //移动到下一个结点
          }
      }
      
      /*
      合并单链表(思想):
          将非递减有序单链表la和lb合并为新的非递减有序单链表lc,要求利用原来表空间
          1. 因为新创建的单链表lc仍是非递减有序,所以使用尾插法创建lc
          2. 设立三个工作指针,指针pa和pb分别指向单链表la和lb的首元结点,指针pc指向链表lc的头结点
          3. 比较pa,pb的数据域大小,将小者插入到lc的表尾,移动指针,直至其中一个链表为空
          4. 将剩余一个链表结点链接到lc的表尾
      */
      
      template<typename elemType>
      linkList<elemType>* linkList<elemType>::Union(linkList<elemType>* lb)
      {
          Node* pa, * pb, * pc;       //分别是指向la,lb,lc的工作指针
          linkList<elemType>* lc = this;       //lc利用la的空间
          pa = head->next;            //la头结点的指针域置为NULL,构成空链表
          head->next = NULL; 
          pb = (lb->next)->next;      //pb指向lb链表的首元结点
          (lb->head)->next = NULL;    //lb头结点的指针域置为NULL,构成空链表
      
          pc = lc->head;              //pc直接利用la的头结点
          while (pa && pb) {
              if (pa->data <= pb->data) {  //pa所指向的结点用尾插法插入到lc链表中
                  pc->next = pa;
                  pc = pa;
                  pa = pa->next;
              }
              else {                  //pb所指向的结点尾插法插入到lc中
                  pc->next = pb;
                  pc = pb;
                  pb = pb->next;
              }
          }
          if (pa) {                   //pa未到表尾,则将pc指向pa
              pc->next = pa;
              lc->next = tail;        //修改表尾指针,lc的表尾即为la的表尾
          }
          else {                        //若pb未到表尾,则将pc指向pb
              pc->next = pb;
              lc->next = lb->tail;    //修改表尾指针,
          }
          lc->curLength = curLength + lb->curLength;
          delete lb;
          return lc;
      
          return nullptr;
      }
      
      int main() {
          //单链表初始化
          linkList<elemType>::linkList();
          linkList<elemType> list;
      
          //头插法建立链表
          list.headCreate();
          cout << "头插法建立链表: ";
          list.traverse();
      
          cout << "链表逆置: ";
          list.inverse();
          list.traverse();
      
          list.insert(2, 1999);
          cout << "插入1999后的单链表: ";
          list.traverse();
          cout << "1999的位序= " << list.search(1999) << endl;
      
          cout << "查找元素10的前驱" << list.prior(10) << endl;
          cout << "删除元素指定位序元素";
          list.remove(3);
          list.traverse();
      
          //尾插法建立链表
          list.tailCreate();
          list.traverse();
          return 0;
      }

       

  • Hash表的操作
    •   
      #include <iostream>
      #include <unordered_map>
      #include <string>
      #include <algorithm>
      //#include <boost/functional/hash.hpp>   // 根据安装路径选择hash.hpp
      #include <tuple>
      
      using namespace std;
      
      
      class package
      {
      public:
          string getName() const { return name; }
          long long getPhone() const { return phone; }
      
          package(string m_name = 0, long long m_pNum = 0);
      
          bool operator== (const package& p) const
          {    return name == p.name &&
                     phone == p.phone; 
          }
      
      private:
          string name;        
          long long phone;        
      };
      
      package::package(string m_name, long long m_pNum)
          : name(m_name), phone(m_pNum) { }
      
      namespace std
      {
          template<>
          struct hash<package>
          {
              size_t operator() (const package& s) const noexcept
              {
                  return  hash<decltype(s.getName())>()(s.getName()) +
                          hash<decltype(s.getPhone())>()(s.getPhone());
                  
                  //auto t = make_tuple(s.getName(), s.getPhone());                                            
                  //size_t value = boost::hash_value(t);         
                  //return value;     // make_tuple(s.getName(), s.getPhone()) 等价于 tuple<string, long long>()(s.getName(), s.getPhone())
              }
          }; // 间接调用原生Hash.
      }
      
      int main()
      {
          unordered_map<package, int> m_map;
      
          package p1{ "Wang", 13399996666};
          package p2{ "Li", 13399993333};
          package p3{ "Zhang", 13399992222};
          package p4{ "Zhou", 13399991111 };
          package p5{ "Wang", 13399996666};
          package p6{ "Wang", 13366669999 };
      
          m_map[p1]++;
          m_map[p2]++;
          m_map[p3]++;
          m_map[p4]++;
          m_map[p5]++;
          m_map[p6]++;
      
          cout << m_map.bucket(p1) << " ";
          cout << m_map.bucket(p2) << " ";
          cout << m_map.bucket(p3) << " ";
          cout << m_map.bucket(p4) << " ";
          cout << m_map.bucket(p5) << " ";
          cout << m_map.bucket(p6) << " " << endl;
      
          return 0;
      }

       

  • 动态规划
  • 堆排序
  • 归并排序
  • 快速排序
  • 队列的操作
    • C++队列queue模板类的定义在头文件中,queue 模板类需要两个模板参数,一个是元素类型,一个容器类型,元素类型是必要的,容 器类型是可选的,默认为deque 类型。
      C++队列Queue是一种容器适配器,它给予程序员一种先进先出(FIFO)的数据结构。

      C++队列Queue类成员函数如下:

      back() 返回最后一个元素

      empty() 如果队列空则返回真

      front() 返回第一个元素

      pop() 删除第一个元素

      push() 在末尾加入一个元素

      size() 返回队列中元素的个数

      queue 的基本操作举例如下:

      queue入队,如例:q.push(x); 将x 接到队列的末端。

      queue出队,如例:q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。

      访问queue队首元素,如例:q.front(),即最早被压入队列的元素。

      访问queue队尾元素,如例:q.back(),即最后被压入队列的元素。

      判断queue队列空,如例:q.empty(),当队列空时,返回true。

      访问队列中的元素个数,如例:q.size()
            
  • 栈的操作
    •   https://blog.csdn.net/zeijierhuang/article/details/100745910
  • 二叉树
posted @ 2020-06-29 13:37  Cassell  阅读(192)  评论(0编辑  收藏  举报