zoukankan      html  css  js  c++  java
  • 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
    • 二叉树
  • 相关阅读:
    2021“MINIEYE杯”中国大学生算法设计超级联赛(2)(1002 I love tree)(树状数组+树链剖分)
    周末随笔_有关变化
    20210808心情随笔
    离开那个傻叉的地方了
    如何建设符合ISO9000要求的企业文控中心
    企业云盘部署极其简单的分布式文件系统的方法
    企业云盘安全机制-文件加密存储与原文存储的优劣
    查看tomcat打开的文件数
    Centos7 Memcached 安装
    centos7 快速安装rabbitmq
  • 原文地址:https://www.cnblogs.com/cassell/p/13207559.html
Copyright © 2011-2022 走看看