绪论
数据结构具体包含三个方面的内容:数据的逻辑结构,数据的储存结构,数据的操作算法
数据:数值数据 非数值数据
数据元素:组成数据的基本单位
数据对象:具有相同性质的数据元素的集合,是数据的子集
数据结构:数据元素及其相互关系的集合
数据类型:一组值的集合以及定义于这个值集上的一组操作的总称
程序=数据结构+算法
提高程序可读性的措施:模块化,变量,标识符含义化,注释,没设全局变量,空格和缩进
逻辑结构:
集合:数据元素属于同一个集合,但没有关系
线性结构:一对一
树结构:一对多
图结构:多对多
储存结构:顺序储存 链接储存
抽象数据类型:
ADT的定义:逻辑结构 操作集合
|
数据结构:储存结构 算法设计
|
类:成员变量 成员函数
算法:计算机求解特定问题的方法和步骤,是指令的有限序列
5个重要特性:
输入 输出 有穷性 确定性 可行性
算法的评价:正确性 健壮性 可读性 高效率 低储存空间需求
算法描述方法:自然语言 流程图 伪代码 程序设计语言
线性表:
特点:数据元素之间仅具有单一的前驱和后继关系,一个线性表中元素的类型必须相同
顺序表:插入 删除O(N)
对一个线性表分别进行遍历和逆置计算,最好的时间复杂度分别
- 遍历:
-
- 时间复杂度:O(n)
- 遍历线性表中的每个元素,需要访问每个元素一次,因此时间复杂度为O(n)。
- 逆置:
-
- 时间复杂度:O(n)
- 最优的逆置算法也需要访问线性表中的每个元素至少一次。一种常见的逆置方法是使用两个指针,分别从两端向中间移动,依次交换对应位置上的元素。这样的操作需要线性时间,因此时间复杂度也是O(n)。
链表:前插O(N) 后插O(1)
线性表可以用顺序表或链表存储,有 n个表同时并存,并且在处理过程中各表的长度会动态发生变化,表的总数也可能自动改变,应选用哪种存储表示,为什么
在处理过程中,线性表的长度会动态变化,并且表的总数也可能发生改变,这种情况下,应该选用链表存储表示。
下面是为什么选择链表存储表示的一些理由:
1. **动态长度
2. **动态总数:
3. **插入和删除效率高:
4. **不需要预先分配固定大小的内存空间:
5. **对内存的高效利用:
6. **容易实现动态扩展和收缩:
如果表的总数基本稳定,且很少进行插入和删除,但要求以最快的速度存取表中的元素,应采用哪种储存表示
如果表的总数基本稳定,且很少进行插入和删除操作,但要求以最快的速度存取表中的元素,那么应该选择使用顺序表作为存储表示。
以下是选择顺序表的一些理由:
1. **快速随机访问:*
2. **缓存友好:*
3. **节省内存:
4. **少量插入和删除操作不会带来太大影响:*
总的来说,如果你的应用场景中,表的总数基本稳定,而且对存取速度要求极高,同时又很少进行插入和删除操作,那么使用顺序表会更为合适。它能够提供快速的随机访问和较高的缓存友好性。
顺序表操作
- #include<iostream>
- using namespace std;
- const int MAXsize = 100;//顺序表最大长度
- //PS:此实现中 顺序表下标从0开始!!
- class ssb
- {
- private:
- int data[MAXsize];
- int lenth;
- public:
- ssb()//无参构造函数
- {
- lenth = 0;
- }
- // 有参构造函数
- ssb(int arr[], int len)
- {
- if (len > MAXsize)
- {
- cout << "非法" << endl;
- return;
- }
- for (int i = 0; i < len; i++) data[i] = arr[i];
- lenth = len;//储存真实长度
- }
- int getlen()const// 获取顺序表长度
- {
- return lenth;
- }
- //按位查找元素
- int findp(int pos)
- {
- if (pos<1 || pos>lenth)
- {
- cout << "查找位置超出范围" << endl;
- return -1;
- }
- return data[pos - 1];
- }
- //按值查找
- int findv(int val)const
- {
- for (int i = 0; i < lenth; i++)
- if (data[i] == val) return i + 1;
- cout << "未找到" << endl;
- return -1;
- }
- // 遍历顺序表
- void tra()const
- {
- cout << "开始遍历" << endl;
- for (int i = 0; i < lenth; i++) cout << data[i] << " ";
- cout << endl;
- }
- // 插入元素,pos为插入位置(从1开始)
- void insert(int val, int pos)
- {
- if (pos<1 || pos>lenth + 1 || lenth == MAXsize)
- {
- cout << "非法" << endl;
- return;
- }
- for (int i = lenth; i >= pos; i--) data[i] = data[i - 1];
- data[pos - 1] = val;
- lenth++;
- }
- // 删除元素,pos为删除位置(从1开始)
- void remove(int pos)
- {
- if (pos<1 || pos>lenth)
- {
- cout << "非法" << endl;
- return;
- }
- for (int i = pos - 1; i < lenth - 1; i++) data[i] = data[i + 1];
- lenth--;
- }
- };
- int main()
- {
- int arr[] = { 1, 2, 3, 4, 5 };
- int len = sizeof(arr) / sizeof(arr[0]);
- ssb l1;
- ssb l2(arr, len);
- cout << "2长" << l2.getlen() << endl;
- cout << l2.findp(3) << endl;
- cout << l2.findv(4) << endl;
- l2.tra();
- l2.insert(6, 2);
- l2.tra();
- l2.remove(4);
- l2.tra();
- return 0;
- }
单链表操作
- #include<iostream>
- using namespace std;
- struct node
- {
- int data;
- node* next;
- node(int val):data(val),next(nullptr){}
- };
- class lb
- {
- private:
- node* head;
- public:
- //无参构造
- lb() :head(nullptr) {}
- //有参构造
- lb(int arr[], int n)
- {
- head = nullptr;//初始化头指针为空
- for (int i = n - 1; i>= 0; i--)
- {
- //逆序插入元素 保证与原序列顺序一致
- node* newn = new node(arr[i]);
- newn->next = head;
- head = newn;
- }
- }
- //析构
- ~lb()
- {
- node* temp;
- while (head != nullptr)
- {
- temp = head;
- head = head->next;
- delete temp;
- }
- }
- //遍历输出
- void tra()
- {
- node* cur = head;
- while (cur != nullptr)
- {
- cout << cur->data << " ";
- cur = cur->next;
- }
- cout << endl;
- }
- //按位查找
- node* findp(int pos)
- {
- if (pos <= 0) return nullptr;
- node* cur = head;
- for (int i = 1; i < pos && cur != nullptr; i++) cur = cur->next;
- return cur;
- }
- //按值查找
- node* findv(int val)
- {
- node* cur = head;
- while (cur != nullptr)
- {
- if (cur->data == val) return cur;
- cur = cur->next;
- }
- return nullptr;
- }
- //在指定位置前插
- void qc(int pos, int val)
- {
- if (pos <= 0) return;
- node* newn = new node(val);
- if (pos == 1)
- {
- //在头部插入 要特判
- newn->next = head;
- head = newn;
- }
- else
- {
- node* cur = head;
- for (int i = 1; i < pos - 1 && cur != nullptr; i++) cur = cur->next;
- if (cur != nullptr)
- {
- newn->next = cur->next;
- cur->next = newn;
- }
- else// 如果指定位置超出链表长度,直接在尾部插入
- {
- node* tail = head;
- while (tail->next != nullptr) tail = tail->next;
- tail->next = newn;
- }
- }
- }
- //在指定位置后插
- void hc(int pos, int val)
- {
- if (pos <= 0) return;
- node* newn = new node(val);
- if (pos == 1)
- {
- newn->next = head;
- head = newn;
- }
- else
- {
- node* cur = head;
- for (int i = 1; i < pos && cur != nullptr; i++) cur = cur->next;
- if (cur != nullptr)
- {
- newn->next = cur->next;
- cur->next = newn;
- }
- }
- }
- //删除指定位置结点
- void de(int pos)
- {
- if (pos <= 0) return;
- if (pos == 1)
- {
- node* tem = head;
- head = head->next;
- delete tem;
- }
- else
- {
- node* cur = head;
- for (int i = 1; i < pos - 1 && cur != nullptr; i++) cur = cur->next;
- if (cur != nullptr && cur->next != nullptr)
- {
- node* pre = nullptr;
- node* cur = head;
- node* ne = nullptr;
- while (cur != nullptr)
- {
- ne = cur->next;
- cur->next = pre;
- pre = cur;
- cur = ne;
- }
- head = pre;
- }
- //有序合并单链表
- void merge(lb& list)
- {
- node* m = nullptr;//合并后的链表头指针,初始为 nullptr
- node* cur = head;//当前链表(调用函数的链表)的指针,初始为该链表的头指针
- node* oth = list.head;//另一链表的指针,初始为另一链表的头指针
- node* mcur = nullptr;//合并链表的当前指针,初始为 nullptr
- while (cur != nullptr || oth != nullptr)
- {
- node* newn = new node(0);
- if (m == nullptr)
- {
- m = newn;
- mcur = m;
- }
- else
- {
- mcur->next = newn;
- mcur = mcur->next;
- }
- if (cur != nullptr && (oth == nullptr || cur->data <= oth->data))
- {
- newn->data = cur->data;
- cur = cur->next;
- }
- else if (oth != nullptr)
- {
- newn->data = oth->data;
- oth = oth->next;
- }
- }
- //释放原链表内存
- node* tem;
- while (head != nullptr)
- {
- tem = head;
- head = head->next;
- delete tem;
- }
- head = m;
- }
- };
- int main()
- {
- int arr1[] = { 1, 3, 5, 7, 9 };
- int arr2[] = { 2, 4, 6, 8, 10 };
- lb l1(arr1, 5);
- lb l2(arr2, 5);
- l1.tra();
- l2.tra();
- l1.merge(l2);
- l1.tra();
- l1.reverse();
- l1.tra();
- l1.qc(2, 19);// 在位置2前插入
- l1.tra();
- l1.hc(4, 60); // 在位置4后插入
- l1.tra();
- l1.de(3);// 删除节点3
- l1.tra();
- return 0;
- }
//双向链表
- #include<iostream>
- using namespace std;
- class node
- {
- public:
- int data;
- node* pre;
- node* next;
- node(int val):data(val),pre(nullptr),next(nullptr){}
- };
- class slb
- {
- private:
- node* head;
- public:
- slb() :head(nullptr) {};
- slb(int arr[], int n)
- {
- head = nullptr;
- for (int i = 0; i < n; i++)
- {
- node* newn = new node(arr[i]);
- if (head == nullptr) head = newn;
- else
- {
- node* cur = head;
- while (cur->next != nullptr) cur = cur->next;
- cur->next = newn;
- newn->pre = cur;
- }
- }
- }
- //求链表长度
- int getlen()const
- {
- int cou = 0;
- node* cur = head;
- while (cur != nullptr)
- {
- cou++;
- cur = cur->next;
- }
- return cou;
- }
- int finp(int pos) const
- {
- if (pos <= 0)
- {
- cout << "非法" << endl;
- return -1;
- }
- node* cur = head;
- for (int i = 1; i < pos && cur != nullptr; i++) cur = cur->next;
- if (cur != nullptr) return cur->data;
- else
- {
- cout << "越界" << endl;
- return -1;
- }
- }
- int finv(int val)const
- {
- node* cur = head;
- int pos = 1;
- while (cur != nullptr)
- {
- if (cur->data == val) return pos;
- cur = cur->next;
- pos++;
- }
- cout << "没找到" << endl;
- return -1;
- }
- void tra()const
- {
- node* cur = head;
- while (cur != nullptr)
- {
- cout << cur->data << " ";
- cur = cur->next;
- }
- cout << endl;
- }
- void qc(int pos, int val)
- {
- if (pos <= 0)
- {
- cout << "非法" << endl;
- return;
- }
- node* newn = new node(val);
- if (pos == 1)
- {
- newn->next = head;
- head->pre = newn;
- head = newn;
- }
- else
- {
- node* cur = head;
- for (int i = 1; i < pos - 1 && cur != nullptr; i++) cur = cur->next;
- if (cur != nullptr)
- {
- newn->next = cur->next;
- newn->pre = cur;
- cur->next->pre = newn;
- cur->next = newn;
- }
- }
- }
- void hc(int pos, int val)
- {
- if (pos <= 0)
- {
- cout << "非法" << endl;
- return;
- }
- node* newn=new node(val);
- if (pos == 1)
- {
- newn->next = head;
- head->pre = newn;
- head = newn;
- }
- else
- {
- node* cur = head;
- for (int i = 1; i < pos && cur != nullptr; i++) cur = cur->next;
- if (cur != nullptr)
- {
- newn->next = cur->next;
- newn->pre = cur;
- cur->next->pre = newn;
- cur->next = newn;
- }
- }
- }
- void de(int val)
- {
- node* cur = head;
- while (cur != nullptr && cur->data != val) cur = cur->next;
- if (cur == nullptr)
- {
- cout << "没找到" << endl;
- return;
- }
- if (cur->pre == nullptr)
- {
- //删除头结点
- head = cur->next;
- if (head != nullptr) head->pre = nullptr;
- }
- else
- {
- cur->pre->next = cur->next;
- cur->next->pre = cur->pre;
- }
- delete cur;
- }
- void rev()
- {
- node* tem = nullptr;
- node* cur = head;
- while (cur != nullptr)
- {
- tem = cur->pre;
- cur->pre = cur->next;
- cur->next = tem;
- cur = cur->pre;
- }
- if (tem != nullptr) head = tem->pre;
- }
- ~slb()
- {
- node* tem;
- while (head != nullptr)
- {
- tem = head;
- head = head->next;
- delete tem;
- }
- }
- };
- int main()
- {
- // 创建双向链表
- int arr[] = { 1, 2, 3, 4, 5 };
- slb l1 = slb(arr, 5);
- l1.tra();
- l1.qc(3, 6);
- l1.tra();
- l1.hc(5, 7);
- l1.tra();
- l1.de(4);
- l1.tra();
- l1.rev();
- l1.tra();
- return 0;
- }
求并集
- #include<iostream>
- #include<unordered_set>
- #include<vector>
- using namespace std;
- class set
- {
- private:
- vector<int>ele;
- public:
- // 添加元素到集合
- void add(int e)
- {
- ele.push_back(e);
- }
- // 获取集合元素
- vector<int>get()const
- {
- return ele;
- }
- // 判断集合是否包含某个元素
- bool contain(int e)const
- {
- for (int x : ele)
- if (x == e) return true;
- return false;
- }
- // 计算集合并集
- set uni(const set& s)const
- {
- set uniset= *this;//创建一个新的集合 unionSet,并将当前集合 *this 的所有元素复制到 unionSet 中。
- //这样做是为了保留当前集合的所有元素,形成一个初始的并集
- for (int e : s.get())//遍历第二个集合 set2 中的所有元素
- if (!contain(e)) uniset.add(e);
- return uniset;
- }
- };
- int main()
- {
- set s1, s2;
- s1.add(1);
- s1.add(2);
- s1.add(3);
- s1.add(4);
- s1.add(5);
- s2.add(3);
- s2.add(4);
- s2.add(5);
- s2.add(6);
- s2.add(7);
- set unis = s1.uni(s2);
- for (int x : unis.get())
- cout << x << " ";
- return 0;
- }
2
- #include<iostream>
- #include<unordered_set>
- #include<vector>
- using namespace std;
- unordered_set<int>uni(const unordered_set<int>& s1, const unordered_set<int>& s2)
- {
- unordered_set<int>uniset = s1;
- for (int x : s2) uniset.insert(x);
- return uniset;
- }
- int main()
- {
- unordered_set<int>s1 = { 1,2,3,4,5 };
- unordered_set<int>s2 = { 3,4,5,6,7 };
- unordered_set<int>re = uni(s1, s2);
- for (int x : s1) cout << x << " ";
- cout << endl;
- for (int x : s2) cout << x << " ";
- cout << endl;
- for (int x : re) cout << x << " ";
- cout << endl;
- }
一元多项式相加
- #include<iostream>
- #include<map>
- using namespace std;
- using poly=map<int, double>;//使用 C++ 中的 using 关键字为 std::map<int, double> 定义了一个别名
- poly add(const poly& p1, const poly& p2)
- {
- poly re;
- for (const auto& t : p1) re[t.first] += t.second;
- for (const auto& t : p2) re[t.first] += t.second;
- return re;
- }
- void print(const poly& p)
- {
- for (const auto& t : p)
- cout << t.second << "x^" << t.first << "+";
- cout << "0" << endl;
- }
- int main()
- {
- poly p1 = { {2, 3.5}, {1, 2.0}, {0, 1.0} };
- poly p2 = { {2, 1.5}, {1, 3.0}, {0, 2.0} };
- poly re = add(p1, p2);
- print(p1);
- print(p2);
- print(re);
- return 0;
- }
将元素为整数的顺序表重新排列以a1为界的两部分,a1前面的值均比a1小,a1后面的值都比a1大,要求算法时间复杂度O(n)
荷兰国旗问题是一种经典的数组排序问题,其中数组中的元素只有三种不同的值。这个问题的目标是将数组按照这三种值分为三个部分,实现排序。这个问题的名字来自于荷兰国旗的颜色有三种,与数组元素的三种值相对应。
- #include <iostream>
- #include <vector>
- /*算法步骤如下:
- 初始化三个指针:low指向表头,high指向表尾,mid指向当前遍历的元素。
- 开始遍历列表:
- 如果arr[mid] < a1,将arr[mid]与arr[low]交换,然后low和mid同时向后移动。
- 如果arr[mid] > a1,将arr[mid]与arr[high]交换,然后high向前移动。
- 如果arr[mid] == a1,只需将mid向后移动。
- 重复上述步骤直到mid指向表尾。*/
- void rearrange(std::vector<int>& arr, int a1) {
- int low = 0;
- int high = arr.size() - 1;
- int mid = 0;
- while (mid <= high) {
- if (arr[mid] < a1) {
- std::swap(arr[low], arr[mid]);
- low++;
- mid++;
- } else if (arr[mid] > a1) {
- std::swap(arr[mid], arr[high]);
- high--;
- } else {
- mid++;
- }
- }
- }
- int main() {
- std::vector<int> arr = {3, 1, 5, 2, 6, 4, 7};
- int a1 = 4;
- rearrange(arr, a1);
- for (int i : arr) {
- std::cout << i << " ";
- }
- std::cout << std::endl;
- return 0;
- }
编写算法,求循环链表中结点个数
- #include <iostream>
- // 定义循环链表的结点结构
- struct ListNode {
- int val;
- ListNode* next;
- ListNode(int x) : val(x), next(NULL) {}
- };
- // 计算循环链表中结点的个数
- int countNodesInCircularLinkedList(ListNode* head) {
- if (head == NULL) {
- return 0; // 如果链表为空,则节点个数为0
- }
- ListNode* current = head; // 从头结点开始遍历
- int count = 0; // 初始化计数器为0
- do {
- count++;
- current = current->next; // 移动到下一个结点
- } while (current != head); // 当遍历回到头结点时停止循环
- return count
已知一个单链表,请编写复制单链表的算法
- #include <iostream>
- // 定义单链表的节点结构
- struct ListNode {
- int val;
- ListNode* next;
- ListNode(int x) : val(x), next(NULL) {}
- };
- // 复制单链表的函数
- ListNode* copyLinkedList(ListNode* head) {
- if (head == NULL) {
- return NULL; // 如果原链表为空,返回空指针
- }
- // 创建一个新链表的头节点,并初始化当前节点指针
- ListNode* newHead = new ListNode(head->val);
- ListNode* currentNew = newHead;
- ListNode* currentOld = head->next;
- // 遍历原链表,复制节点
- while (currentOld != NULL) {
- // 复制节点值
- currentNew->next = new ListNode(currentOld->val);
- currentNew = currentNew->next;
- currentOld = currentOld->next;
- }
- return newHead;
- }
- int main() {
- // 创建一个单链表:1 -> 2 -> 3 -> 4
- ListNode* head = new ListNode(1);
- head->next = new ListNode(2);
- head->next->next = new ListNode(3);
- head->next->next->next = new ListNode(4);
- // 调用复制链表的函数
- ListNode* newHead = copyLinkedList(head);
- // 输出新链表的节点值
- ListNode* current = newHead;
- while (current != NULL) {
- std::cout << current->val << " ";
- current = current->next;
- }
- std::cout << std::endl;
- // 释放内存
- delete head->next->next->next;
- delete head->next->next;
- delete head->next;
- delete head;
- // 释放新链表内存
- current = newHead;
- while (current != NULL) {
- ListNode* temp = current;
- current = current->next;
- delete temp;
- }
- return 0;
- }
c++已知一个无序单链表,表中结点的data字段为正整数,编写算法按递增顺序打印表中结点的值,写出完整程序和示例,注意内存管理
这个程序首先定义了一个单链表的结构体 ListNode
,然后实现了三个函数:
insertSorted
:将一个新节点按递增顺序插入到已排序链表中。printSorted
:按递增顺序打印链表节点的值。deleteList
:释放链表内存。
在 main
函数中,我们创建了一个无序单链表,然后创建了一个新的已排序链表的头节点。接着,遍历无序链表,将节点插入已排序链表。最后,打印已排序链表的节点值,并释放内存以避免内存泄漏。
- #include <iostream>
- // 定义单链表的节点结构
- struct ListNode {
- int data;
- ListNode* next;
- ListNode(int x) : data(x), next(NULL) {}
- };
- // 插入新节点到已排序链表中
- void insertSorted(ListNode*& sortedHead, ListNode* newNode) {
- if (sortedHead == NULL || sortedHead->data >= newNode->data) {//更新头结点
- newNode->next = sortedHead;
- sortedHead = newNode;
- }
- else {
- ListNode* current = sortedHead;
- while (current->next != NULL && current->next->data < newNode->data) {
- current = current->next;
- }
- newNode->next = current->next;
- current->next = newNode;
- }
- }
- // 按递增顺序打印链表节点的值
- void printSorted(ListNode* head) {
- while (head != NULL) {
- std::cout << head->data << " ";
- head = head->next;
- }
- std::cout << std::endl;
- }
- // 释放链表内存
- void deleteList(ListNode* head) {
- while (head != NULL) {
- ListNode* temp = head;
- head = head->next;
- delete temp;
- }
- }
- int main() {
- // 创建一个无序单链表:3 -> 1 -> 4 -> 2
- ListNode* head = new ListNode(3);
- head->next = new ListNode(1);
- head->next->next = new ListNode(4);
- head->next->next->next = new ListNode(2);
- // 创建一个新的已排序链表的头节点
- ListNode* sortedHead = NULL;
- // 遍历无序链表,并将节点插入已排序链表
- ListNode* current = head;
- while (current != NULL) {
- ListNode* newNode = new ListNode(current->data);
- insertSorted(sortedHead, newNode);
- current = current->next;
- }
- // 打印已排序链表的节点值
- printSorted(sortedHead);
- // 释放内存
- deleteList(head);
- deleteList(sortedHead);
- return 0;
- }
栈和队列
栈是只能在一端进行插入或删除的线性表
允许插入,删除的一端称为栈顶,另一端称为栈底
栈中没有元素时称为空栈
顺序栈的操作
- #include<iostream>
- #include<vector>
- using namespace std;
- class stack
- {
- //栈顶是vector的back元素
- private:
- vector<int>data;
- public:
- //初始化栈
- stack() {}
- //入栈操作
- void push(int val)
- {
- data.push_back(val);
- }
- //出栈操作
- void pop()
- {
- if (!isempty()) data.pop_back();
- else cout << "已经空了" << endl;
- }
- //取栈顶元素
- int top()const
- {
- if (!isempty()) return data.back();
- else
- {
- cout << "栈空" << endl;
- return -1;
- }
- }
- //判断栈是否为空
- bool isempty() const
- {
- return data.empty();
- }
- };
- int main()
- {
- stack s;
- s.push(1);
- s.push(2);
- s.push(3);
- cout << "栈顶元素" << s.top() << endl;
- s.pop();
- cout << "栈顶元素" << s.top() << endl;
- while (!s.isempty()) s.pop();
- cout << "栈顶元素" << s.top() << endl;
- return 0;
- }
链栈的操作
- #include<iostream>
- #include<list>
- using namespace std;
- //个人感觉就是把链表竖起来放
- class linkstcak
- {
- private:
- //定义链栈结点
- struct node
- {
- int data;
- node* next;
- node(int val):data(val),next(nullptr){}
- };
- node* topnode;//栈顶指针
- public:
- //判断栈是否为空
- bool isempty() const
- {
- return topnode == nullptr;
- }
- //初始化链栈
- linkstcak():topnode(nullptr){}
- //入栈操作
- void push(int val)
- {
- node* newn = new node(val);
- newn->next = topnode;
- topnode = newn;
- }
- //出栈操作
- void pop()
- {
- if (!isempty())
- {
- node* tem = topnode;
- topnode = topnode->next;
- delete tem;
- }
- else cout << "栈空" << endl;
- }
- //取栈顶元素
- int top() const
- {
- if (!isempty()) return topnode->data;
- else
- {
- cout << "栈空" << endl;
- return -1;
- }
- }
- //析构
- ~linkstcak()
- {
- while (topnode != nullptr)
- {
- node* tem = topnode;
- topnode = topnode->next;
- delete tem;
- }
- }
- };
- int main()
- {
- linkstcak s;
- s.push(1);
- s.push(2);
- s.push(3);
- cout << s.top() << endl;
- s.pop();
- cout << s.top() << endl;
- return 0;
- }
栈的应用:
算符优先法求算数表达式的值
- #include<iostream>
- #include<stack>
- #include<unordered_map>
- #include<cctype>//isspace(ch), isdigit(ch)
- #include<string>
- using namespace std;
- //定义运算符优先级
- unordered_map<char,int> prece= { {'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}, {'^', 3} };
- //判断字符是否为运算符
- bool isop(char ch)
- {
- return prece.find(ch) != prece.end();
- }
- //比较运算符优先级
- int com(char op1, char op2)
- {
- return prece[op1] - prece[op2];
- }
- //计算二元运算
- int ap(int a, char op, int b)
- {
- switch (op)
- {
- case '+': return a + b;
- case '-': return a - b;
- case '*': return a * b;
- case '/': return a / b;
- case'^':return static_cast<int>(pow(a, b));
- //通过 static_cast<int> 将其转换为 int 类型,即取 double 类型的结果的整数部分。
- default:return 0;
- }
- }
- //计算算数表达式的值
- int eva(const string& ex)
- {
- stack<int>rands;//操作数
- stack<char>ops;//运算符
- for (char ch : ex)
- {
- if (isspace(ch)) continue;
- else if (isdigit(ch)) rands.push(ch - '0');// 处理数字
- else if (isop(ch)) // 处理运算符
- {
- while (!ops.empty() && com(ops.top(), ch) >= 0)
- {//如果运算符栈不为空且当前运算符的优先级小于等于栈顶运算符的优先级,
- //执行计算,将结果推入 operands 栈,直到满足条件
- int b = rands.top(); rands.pop();
- int a = rands.top(); rands.pop();
- char op = ops.top(); ops.pop();
- rands.push(ap(a, op, b));
- }
- ops.push(ch);//将当前运算符推入 operators 栈。
- }
- else if (ch == '(') ops.push(ch);//左括号入栈
- else if (ch == ')')
- {
- while (!ops.empty() && ops.top() != '(')
- {//如果字符是闭括号 ')',执行计算,直到遇到与之匹配的开括号 '(',
- //并将结果推入 operands 栈
- int b = rands.top(); rands.pop();
- int a = rands.top(); rands.pop();
- char op = ops.top(); ops.pop();
- rands.push(ap(a, op, b));
- }
- ops.pop();//弹出左括号
- }
- }
- //处理剩余运算符 可能是嵌套的括号之类的
- while (!ops.empty())
- {
- int b = rands.top(); rands.pop();
- int a = rands.top(); rands.pop();
- char op = ops.top(); ops.pop();
- rands.push(ap(a, op, b));
- }
- //返回最终结果
- return rands.top();
- }
- int main()
- {
- string ex;
- getline(cin, ex);
- int re = eva(ex);
- cout << re << endl;
- return 0;
- }
递归的含义:函数直接调用自己或者通过一系列调用语句间接调用自己
递归的条件:
大问题可以分解为小问题
可以确定递归到何时为止
求阶乘的递归算法
int fact(int n)
{
if (n == 0) return 1;
else return n * fact(n - 1);
}
编号为1 2 3 4 5的5辆列车顺序开进一个栈式结构的站点,问开出车站的顺序有多少种可能,请具体写出所有可能的出栈序列
可以使用递归的方法来解决
- #include <iostream>
- #include <vector>
- void generatePermutations(vector<int> inStack, vector<int> outStack, vector<int> result) {
- // 如果输入栈和输出栈都为空,表示一种可能的出栈顺序
- if (inStack.empty() && outStack.empty()) {
- for (int num : result) cout << num << " ";
- cout << endl;
- return;
- }
- // 尝试将一个车厢从输入栈移到输出栈
- if (!inStack.empty()) {
- std::vector<int> newInStack = inStack;
- std::vector<int> newOutStack = outStack;
- std::vector<int> newResult = result;
- newOutStack.push_back(newInStack.back());
- newInStack.pop_back();
- generatePermutations(newInStack, newOutStack, newResult);
- }
- // 尝试将一个车厢从输出栈中出栈
- if (!outStack.empty()) {
- std::vector<int> newInStack = inStack;
- std::vector<int> newOutStack = outStack;
- std::vector<int> newResult = result;
- newResult.push_back(newOutStack.back()); //在容器的尾部添加一个元素的函数
- newOutStack.pop_back(); //删除容器的尾部元素的函数
- generatePermutations(newInStack, newOutStack, newResult);
- }
- }
- int main() {
- std::vector<int> inStack = {1, 2, 3, 4, 5};
- std::vector<int> outStack;
- std::vector<int> result;
- generatePermutations(inStack, outStack, result);
- return 0;
- }
利用栈实现把十进制整数转换为二进制至十六进制之间的任一进制数并输出的功能
- #include <iostream>
- #include <stack>
- #include <string>
- // 将十进制数转换为其他进制
- std::string decimalToBase(int num, int base) {
- std::stack<char> result;
- while (num > 0) {
- int remainder = num % base;
- char digit;
- if (remainder < 10) {
- digit = '0' + remainder;
- } else {
- digit = 'A' + remainder - 10;
- }
- result.push(digit);
- num /= base;
- }
- std::string converted;
- while (!result.empty()) {
- converted += result.top();
- result.pop();
- }
- return converted;
- }
- int main() {
- int decimalNumber;
- std::cout << "输入一个十进制整数: ";
- std::cin >> decimalNumber;
- std::string binary = decimalToBase(decimalNumber, 2);
- std::string octal = decimalToBase(decimalNumber, 8);
- std::string hexadecimal = decimalToBase(decimalNumber, 16);
- std::cout << "二进制表示: " << binary << std::endl;
- std::cout << "八进制表示: " << octal << std::endl;
- std::cout << "十六进制表示: " << hexadecimal << std::endl;
- return 0;
- }
设有一维数组stack[],将其分配给两个栈s1和s2使用,试问如何分配数组空间,使得对任何一个栈,当且仅当数组空间全满时才不能插入,分别给出两个栈的出栈入栈算法
要将一个一维数组 `stack` 分配给两个栈 `s1` 和 `s2`,并使得只有当数组空间全满时才不能插入,可以采取以下策略:
1. 将数组的两端作为两个栈的起始点,分别向中间延伸。假设数组长度为 `n`,则 `s1` 从数组的左端开始, `s2` 从数组的右端开始,它们向中间延伸。
2. 为了使得两个栈的空间都能够被充分利用,可以让它们分别从两端向中间延伸,直到它们的栈顶指针相遇为止。
3. 当两个栈的栈顶指针相遇时,表示数组空间已经被完全利用,此时再进行插入操作会导致栈溢出。
以下是两个栈的基本操作:
**栈s1:**
- `s1_top`: 指向栈 `s1` 的栈顶元素位置。
- `s1_push(x)`: 将元素 `x` 压入栈 `s1`,操作前需判断 `s1_top` 是否等于 `s2_top`,若相等则栈满。
- `s1_pop()`: 弹出栈 `s1` 的栈顶元素,操作前需判断 `s1_top` 是否等于 `-1`,若等于 `-1` 则栈空。
**栈s2:**
- `s2_top`: 指向栈 `s2` 的栈顶元素位置。
- `s2_push(x)`: 将元素 `x` 压入栈 `s2`,操作前需判断 `s2_top` 是否等于 `s1_top`,若相等则栈满。
- `s2_pop()`: 弹出栈 `s2` 的栈顶元素,操作前需判断 `s2_top` 是否等于 `n`,若等于 `n` 则栈空。
具体实现可以采用相应的数据结构(比如数组)以及相应的指针来管理两个栈的操作。需要注意的是,在进行任何操作前,都需要进行相应的栈满或栈空检查,以保证程序的健壮性。
- #include <iostream>
- using namespace std;
- const int MAX_SIZE = 100; // 假设数组大小为100
- class TwoStacks {
- private:
- int stack[MAX_SIZE];
- int s1_top; // 栈1的栈顶指针
- int s2_top; // 栈2的栈顶指针
- public:
- TwoStacks() {
- s1_top = -1; // 初始化栈1的栈顶指针
- s2_top = MAX_SIZE; // 初始化栈2的栈顶指针
- }
- void s1_push(int x) {
- if (s1_top + 1 == s2_top) {
- cout << "栈满,无法插入" << endl;
- return;
- }
- stack[++s1_top] = x;
- }
- void s2_push(int x) {
- if (s2_top - 1 == s1_top) {
- cout << "栈满,无法插入" << endl;
- return;
- }
- stack[--s2_top] = x;
- }
- int s1_pop() {
- if (s1_top == -1) {
- cout << "栈1为空" << endl;
- return -1; // 返回一个特定值表示栈为空
- }
- return stack[s1_top--];
- }
- int s2_pop() {
- if (s2_top == MAX_SIZE) {
- cout << "栈2为空" << endl;
- return -1; // 返回一个特定值表示栈为空
- }
- return stack[s2_top++];
- }
- };
- int main() {
- TwoStacks ts;
- ts.s1_push(1);
- ts.s1_push(2);
- ts.s2_push(3);
- ts.s2_push(4);
- cout << "s1出栈结果:" << ts.s1_pop() << endl;
- cout << "s2出栈结果:" << ts.s2_pop() << endl;
- return 0;
- }
假设表达式中允许包含3种括号:圆括号,方括号和大括号,试编写一个算法,检查表达式中括号是否配对,能全部配对返回1,否则返回0
- #include <iostream>
- #include <stack>
- #include <string>
- int checkBracketPairing(const std::string& expression) {
- std::stack<char> brackets;
- for (char c : expression) {
- if (c == '(' || c == '[' || c == '{') {
- brackets.push(c);
- } else if (c == ')' || c == ']' || c == '}') {
- if (brackets.empty()) {
- return 0; // 遇到右括号但栈为空,不配对
- }
- char top = brackets.top();
- brackets.pop();
- if ((c == ')' && top != '(') ||
- (c == ']' && top != '[') ||
- (c == '}' && top != '{')) {
- return 0; // 括号不匹配
- }
- }
- }
- return brackets.empty() ? 1 : 0;
- }
- int main() {
- std::string expression1 = "{[()]}";
- std::string expression2 = "{[(])}";
- int result1 = checkBracketPairing(expression1);
- int result2 = checkBracketPairing(expression2);
- std::cout << "Result for expression1: " << result1 << std::endl;
- std::cout << "Result for expression2: " << result2 << std::endl;
- return 0;
- }
队列
队列是一种在一段进行插入,在另一端进行删除的线性表
允许插入的叫队尾
允许删除的叫队头
循环队列的操作
- #include<iostream>
- using namespace std;
- const int MAX = 5;//定义循环队列的最大容量
- class ccq
- {
- private:
- int front;//队头
- int rear;//队尾
- int arr[MAX];
- public:
- ccq():front(-1),rear(-1){}//初始化循环队列
- //判断队列是否为空
- bool isempty()
- {
- return front == -1 && rear == -1;
- }
- //判读队列是否为满 ************************
- bool isfull()
- {
- return (rear + 1) % MAX == front;
- }
- //入队
- void en(int e)
- {
- if (isfull())
- {
- cout << "满了" << endl;
- return;
- }
- if (isempty())
- front = rear = 0;//第一个元素入队
- else
- rear = (rear + 1) % MAX;//队尾后移
arr[rear] = e;//将元素放入队尾
- }
- //出队
- void de()
- {
- if (isempty())
- {
- cout << "空的" << endl;
- return;
- }
- if (front == rear) front = rear = -1;//队列中只有一个元素,出队后队列变为空
- else front = (front + 1) % MAX;//队头后移
- }
- };
- int main()
- {
- return 0;
- }
若循环队列以数组Q【0····m-1】作为其存储结构,变量rear表示循环队列中队尾元素的实际位置,其移动按rear=(rear+1)mod m进行,变量length表示当前循环队列的元素的个数,则循环队列的对首元素的实际位置是
(1+rear+m-length)mod m其中加一是因为需要从队尾的后一个元素算起,不加一的话就会使队尾数据也纳入空队列的计算中
链队列的操作
- #include<iostream>
- using namespace std;
- struct node
- {
- int data;
- node* next;
- node(int val) :data(val), next(nullptr) {}
- };
- class lq
- {
- private:
- node* front;//队头指针
- node* rear;//队尾指针
- public:
- //初始化链队列
- lq() :front(nullptr), rear(nullptr) {}
- //判断链队是否为空
- bool isempty()
- {
- return front == nullptr;
- }
- //入队操作
- void en(int e)
- {
- node* newn = new node(e);
- if (isempty()) front = rear = newn;//队列为空,新节点成为队头和队尾
- else
- {//新节点放入队尾,更新队尾指针
- rear->next = newn;
- rear = newn;
- }
- }
- void de()
- {
- if (isempty())
- {
- cout << "空的" << endl;
- return;
- }
- node* tem = front;
- if (front == rear) front = rear = nullptr;//只有一个节点 删了后为空队列
- else front = front->next;
- delete tem;
- }
- };
队列的应用:
队列求解报数问题
- #include<iostream>
- #include<queue>
- using namespace std;
- void jo(int n, int m)
- {
- queue<int>q;
- // 初始化队列,将所有人编号入队
- for (int i = 1; i <= n; i++) q.push(i);
- // 开始报数
- while (!q.empty())
- {
- // 报数到m的人出列
- for (int i = 1; i <= m; i++)
- {
- int front = q.front();
- q.pop();
- q.push(front);
- }
- cout << q.front()<<" ";
- q.pop();
- }
- }
- int main()
- {
- int n, m;
- cin >> n >> m;
- jo(n, m);
- return 0;
- }
约瑟夫问题
假设以一维数组data[m]储存循环队列的元素,若要使这m个分量都得到应用,则另设一辅助标志变量flag判断队列的状态为空还是满,试编写入队和出队算法
- const int MAX_SIZE = 100; // 假设队列的最大容量为100
- class CircularQueue {
- private:
- int data[MAX_SIZE];
- int front; // 队头指针
- int rear; // 队尾指针
- int flag; // 队列状态标志,0表示空,1表示满
- public:
- CircularQueue() {
- front = rear = 0;
- flag = 0; // 初始状态为队列空
- }
- bool isEmpty() {
- return flag == 0;
- }
- bool isFull() {
- return flag == 1;
- }
- void enqueue(int x) {
- if (isFull()) {
- std::cout << "队列已满,无法入队" << std::endl;
- return;
- }
- data[rear] = x;
- rear = (rear + 1) % MAX_SIZE;
- if (rear == front) {
- flag = 1; // 队列满
- }
- }
- int dequeue() {
- if (isEmpty()) {
- std::cout << "队列为空,无法出队" << std::endl;
- return -1; // 返回一个特定值表示队列为空
- }
- int value = data[front];
- front = (front + 1) % MAX_SIZE;
- flag = 0; // 出队后队列状态为非满
- return value;
- }
- };
假设以带头结点的循环链表表示队列,并且只设一个表尾指针,试编写相应的置队列空,入队和出队操作
- #include <iostream>
- using namespace std;
- // 链表结点定义
- struct ListNode {
- int val;
- ListNode* next;
- ListNode(int x) : val(x), next(NULL) {}
- };
- class CircularQueue {
- private:
- ListNode* rear; // 队尾指针,指向链表的最后一个节点
- public:
- // 构造函数
- CircularQueue() {
- rear = NULL;
- }
- // 置队列空
- void makeEmpty() {
- if (rear == NULL) return; // 队列已空
- ListNode* temp = rear->next; // 头结点
- rear->next = NULL; // 尾节点指向空
- while (temp != NULL) {
- ListNode* nextNode = temp->next;
- delete temp;
- temp = nextNode;
- }
- rear = NULL; // 重置rear
- }
- // 入队
- void enqueue(int x) {
- ListNode* newNode = new ListNode(x);
- if (rear == NULL) {
- rear = newNode;
- rear->next = rear; // 链接到自身形成环
- } else {
- newNode->next = rear->next;
- rear->next = newNode;
- rear = newNode;
- }
- }
- // 出队
- int dequeue() {
- if (rear == NULL) {
- cout << "队列为空,无法出队" << endl;
- return -1; // 返回一个特定值表示队列为空
- }
- ListNode* front = rear->next;
- int value = front->val;
- if (front == rear) {
- rear = NULL; // 队列中只有一个节点,出队后置空rear
- } else {
- rear->next = front->next;
- }
- delete front;
- return value;
- }
- };
- int main() {
- CircularQueue queue;
- queue.enqueue(1);
- queue.enqueue(2);
- queue.enqueue(3);
- while (true) {
- int value = queue.dequeue();
- if (value == -1) break; // 队列为空时跳出循环
- cout << "出队元素: " << value << endl;
- }
- return 0;
- }
如何用两个栈实现队列,写出队列基本操作的算法
使用两个栈可以实现队列的基本操作。一个栈用于入队操作,另一个栈用于出队操作。具体步骤如下:
1. **入队操作**:
- 将元素压入第一个栈(称为 `stack1`)中。
2. **出队操作**:
- 如果第二个栈(称为 `stack2`)不为空,直接从 `stack2` 弹出栈顶元素。
- 如果 `stack2` 为空,将 `stack1` 中的所有元素依次弹出并压入 `stack2`,然后从 `stack2` 弹出栈顶元素。
下面是基于C++的实现:
```cpp
#include <iostream>
#include <stack>
using namespace std;
class MyQueue {
private:
stack<int> stack1; // 用于入队
stack<int> stack2; // 用于出队
void moveElements() {
while (!stack1.empty()) {
stack2.push(stack1.top());
stack1.pop();
}
}
public:
void enqueue(int x) {
stack1.push(x);
}
int dequeue() {
if (stack2.empty()) {
moveElements();
}
if (!stack2.empty()) {
int front = stack2.top();
stack2.pop();
return front;
} else {
cout << "队列为空" << endl;
return -1; // 返回一个特定值表示队列为空
}
}
};
int main() {
MyQueue queue;
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
cout << "出队元素: " << queue.dequeue() << endl;
cout << "出队元素: " << queue.dequeue() << endl;
cout << "出队元素: " << queue.dequeue() << endl;
cout << "出队元素: " << queue.dequeue() << endl; // 队列为空
return 0;
}
```
在这段代码中,我们定义了一个 `MyQueue` 类,其中包括了入队和出队操作。具体实现如下:
- `enqueue` 将元素压入 `stack1` 中。
- `dequeue` 首先检查 `stack2` 是否为空,如果不为空则直接从 `stack2` 中弹出栈顶元素。如果 `stack2` 为空,调用 `moveElements` 函数将 `stack1` 中的元素移动到 `stack2` 中,然后再从 `stack2` 弹出栈顶元素。
在 `main` 函数中,我们创建了一个 `MyQueue` 对象 `queue`,并进行了入队和出队操作。出队操作会依次输出出队的元素。当队列为空时,会输出相应的提示信息。
- #include <iostream>
- #include <stack>
- using namespace std;
- class MyQueue {
- private:
- stack<int> stack1; // 用于入队
- stack<int> stack2; // 用于出队
- void moveElements() {
- while (!stack1.empty()) {
- stack2.push(stack1.top());
- stack1.pop();
- }
- }
- public:
- void enqueue(int x) {
- stack1.push(x);
- }
- int dequeue() {
- if (stack2.empty()) {
- moveElements();
- }
- if (!stack2.empty()) {
- int front = stack2.top();
- stack2.pop();
- return front;
- } else {
- cout << "队列为空" << endl;
- return -1; // 返回一个特定值表示队列为空
- }
- }
- };
- int main() {
- MyQueue queue;
- queue.enqueue(1);
- queue.enqueue(2);
- queue.enqueue(3);
- cout << "出队元素: " << queue.dequeue() << endl;
- cout << "出队元素: " << queue.dequeue() << endl;
- cout << "出队元素: " << queue.dequeue() << endl;
- cout << "出队元素: " << queue.dequeue() << endl; // 队列为空
- return 0;
- }