**【前排提示】**掌握了基本语法之后自己能写出相应的代码,然后编写主函数调试,这种学习方法能起到最佳的复习效果。以下代码仅供参考。哪道题有问题给做个标记,我接下来详细讲解。
- 本节看似内容很多,但总体难度偏低,因此掌握算法的基本思想即可,冲击高分的同学还是尽量把代码都自己实现一遍。
- 大部分题目都给出了两种参考答案,王道和我的,希望同学们也能自己写出更好的代码!这对你的能力提升帮助很大。
顺序表
#include <iostream>
using namespace std;
// 使用动态数组实现线性表
struct List {
int *arr;
int length;
};
// 构造线性表
List createList() {
List list;
list.length = 0;
list.arr = new int[100];
return list;
}
// 插入操作
void insert(List &list, int index, int value) {
if (list.length == 100) {
cout << "线性表已满" << endl;
return;
}
for (int i = list.length - 1; i >= index; i--) {
list.arr[i + 1] = list.arr[i];
}
list.arr[index] = value;
list.length++;
}
// 查找操作
int search(List list, int value) {
for (int i = 0; i < list.length; i++) {
if (list.arr[i] == value) return i;
}
return -1;
}
// 输出所有元素
void print(List list) {
for (int i = 0; i < list.length; i++) {
cout << list.arr[i] << " ";
}
cout << endl;
}
int main() {
List list = createList();
insert(list, 0, 1);
insert(list, 1, 2);
print(list);
int index = search(list, 2);
cout << index << endl;
return 0;
}
单链表
#include <iostream>
using namespace std;
// 节点定义
struct Node {
int val;
Node *next;
Node(int x) : val(x), next(NULL) {}
};
// 头插法建立单链表
Node *CreateListByHead(int arr[], int n) {
Node *head = NULL;
for (int i = 0; i < n; i++) {
Node *node = new Node(arr[i]);
if (head == NULL) {
head = node;
} else {
node->next = head;
head = node;
}
}
return head;
}
// 带虚拟头节点的头插法
Node *CreateListWithHead(int arr[], int n) {
// 创建虚拟头节点
Node *dummy = new Node(-1);
for (int i = 0; i < n; i++) {
// 创建新节点
Node *node = new Node(arr[i]);
// 将新节点插入到表头
node->next = dummy->next;
dummy->next = node;
}
return dummy->next;
}
// 尾插法建立单链表
Node *CreateListByTail(int arr[], int n) {ww
Node *head = nullptr;
Node *tail = nullptr;
for (int i = 0; i < n; i++) {
// 创建新节点
Node *node = new Node(arr[i]);
// 如果链表为空
if (head == nullptr) {
head = node;
tail = node;
} else {
// 将新节点置为尾节点
tail->next = node;
tail = node;
}
}
return head;
}
// 带虚拟头节点的尾插法
Node *CreateListWithTail(int arr[], int n) {
// 创建虚拟头节点
Node *dummy = new Node(-1);
Node *tail = dummy;
for (int i = 0; i < n; i++) {
// 创建新节点
Node *node = new Node(arr[i]);
// 将新节点置为尾节点
tail->next = node;
tail = node;
}
return dummy->next;
}
// 按序号查找节点
Node *getNodeByIndex(Node *head, int i)
{
Node *p = head;
while (i --) p = p->next;
return p;
}
// 按值查找节点
Node *getNodeByValue(Node *head, int e)
{
for (Node *p = head; p; p = p->next)
if (p->val == e)
return p;
return NULL;
}
// 在下表为index的位置上插入值为x的新节点
// 先找到index - 1的节点,再在其后插入新节点
void insertNode(Node* head, int index, int x)
{
Node *p = head;
for (int i = 0; i < index - 1; i ++)
p = p->next;
Node *newNode = new Node(x);
newNode->next = p->next;
p->next = newNode;
}
// 删除链表下标为index的节点
// 先找到index - 1的节点,再删除第index的节点
void deleteNode(Node* head, int index)
{
Node *p = head;
for (int i = 0; i < index - 1; i ++)
p = p->next;
p->next = p->next->next;
}
// 遍历链表
void print(Node *head)
{
for (Node *p = head; p; p = p->next)
cout << p->val << ' ';
cout << endl;
}
int main()
{
int a[] = {1, 5, 6, 2, 9, 2};
int n = sizeof(a) / sizeof(int);
Node *head = CreateListByTail(a, n);
print(head);
insertNode(head, 2, 0);
print(head);
deleteNode(head, 2);
print(head);
return 0;
}
静态链表:
const int N = 10010;
struct Node
{
int val;
int next;
} q[N];
王道代码题
【前排提示】
-
顺序表不用按照王道上的写法来,直接用
数组
或vector
替代即可。 -
考试的时候要求编写完整代码,且有固定的输入输出格式,而非单独一个函数。
-
王道的代码有些比较繁琐,所以要有自己的思考和判断。
顺序表
#include <iostream>
using namespace std;
int del_min(int nums[], int n)
{
if (!n)
{
puts("error!");
return -1;
}
int min_value = 2147483647, pos = -1; // 2^31 - 1
for (int i = 0; i < n; i ++)
{
if (nums[i] < min_value)
{
min_value = nums[i];
pos = i;
}
}
nums[pos] = nums[n - 1];
return min_value;
}
int main()
{
int a[] = {1, 4, 2, 5, 7, 3, 2}, n = sizeof(a) / sizeof(int);
cout << del_min(a, n) << endl;
for (int i = 0; i < n - 1; i ++)
cout << a[i] << ' ';
cout << endl;
return 0;
}
1
2 4 2 5 7 3
#include <iostream>
using namespace std;
void reverse(int nums[], int n)
{
for (int i = 0; i < n >> 1; i ++) // i < n / 2
swap(nums[i], nums[n - i - 1]);
}
int main()
{
int a[] = {1, 4, 2, 5, 7, 3, 2}, n = sizeof(a) / sizeof(int);
reverse(a, n);
for (int i = 0; i < n; i ++)
cout << a[i] << ' ';
cout << endl;
return 0;
}
2 3 7 5 2 4 1
此题包括接下来的几道题目,用双指针都是更好的解决办法。
#include <iostream>
using namespace std;
// 双指针
void del_x(int nums[], int &n, int x)
{
int j = 0;
for (int i = 0; i < n; i ++)
{
if (nums[i] == x) continue;
nums[j] = nums[i];
j ++;
}
n = j; // 截断数组(修改数组长度)
}
int main()
{
int a[] = {1, 4, 2, 5, 7, 3, 2}, n = sizeof(a) / sizeof(int);
del_x(a, n, 2);
for (int i = 0; i < n; i ++)
cout << a[i] << ' ';
cout << endl;
return 0;
}
1 4 5 7 3
#include <iostream>
using namespace std;
// 双指针,跟上一题几乎一样
void del(int nums[], int &n, int s, int t)
{
if (s >= t || !n)
{
puts("error!");
return;
}
int j = 0;
for (int i = 0; i < n; i ++)
{
if (nums[i] >= s && nums[i] <= t) continue;
nums[j] = nums[i];
j ++;
}
n = j;
}
int main()
{
int a[] = {1, 4, 2, 5, 7, 3, 2}, n = sizeof(a) / sizeof(int);
del(a, n, 3, 5);
for (int i = 0; i < n; i ++)
cout << a[i] << ' ';
cout << endl;
return 0;
}
1 2 7 2
#include <iostream>
using namespace std;
// 双指针
void del_dup(int nums[], int &n)
{
if (!n)
{
puts("error!");
return;
}
int j = 1;
for (int i = 1; i < n; i ++)
{
if (nums[i] == nums[i - 1]) continue;
nums[j] = nums[i];
j ++;
}
n = j;
}
int main()
{
int a[] = {1, 1, 2, 2, 3, 3, 3, 3, 4}, n = sizeof(a) / sizeof(int);
del_dup(a, n);
for (int i = 0; i < n; i ++)
cout << a[i] << ' ';
cout << endl;
return 0;
}
1 2 3 4
#include <iostream>
#include <vector>
using namespace std;
vector<int> merge(vector<int> &a, vector<int> &b)
{
vector<int> res;
int i = 0, j = 0;
while (i < a.size() && j < b.size())
{
if (a[i] <= b[j]) res.push_back(a[i ++]);
else res.push_back(b[j ++]);
}
while (i < a.size()) res.push_back(a[i ++]);
while (j < b.size()) res.push_back(b[j ++]);
return res;
}
int main()
{
vector<int> a = {1, 3, 5, 6, 6};
vector<int> b = {2, 4, 5, 7, 9, 9};
vector<int> res = merge(a, b);
for (int i = 0; i < res.size(); i ++)
cout << res[i] << ' ';
cout << endl;
return 0;
}
1 2 3 4 5 5 6 6 7 9 9
此题题目质量一般,不必深究。
#include <iostream>
#include <vector>
using namespace std;
vector<int> func(vector<int>& nums, int m, int n)
{
vector<int> res;
for (int i = 0; i < n; i ++) res.push_back(nums[m + i]);
for (int i = 0; i < m; i ++) res.push_back(nums[i]);
return res;
}
int main()
{
vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8};
int m = 3, n = 5;
vector<int> res = func(nums, m, n);
for (int x : res)
cout << x << ' ';
cout << endl;
return 0;
}
4 5 6 7 8 1 2 3
#include <iostream>
#include <vector>
using namespace std;
void func(vector<int> &nums, int x)
{
int low = 0, high = nums.size() - 1;
while (low <= high)
{
int mid = low + high >> 1;
if (nums[mid] == x)
{
if (mid + 1 < nums.size())
swap(nums[mid + 1], nums[mid]);
return;
}
else if (x >= nums[mid]) low = mid + 1;
else high = mid - 1;
}
// 插入到对应位置
// 应该在下标为low的位置插入x
nums.resize(nums.size() + 1);
for (int i = nums.size() - 1; i > low; i --)
nums[i] = nums[i - 1];
nums[low] = x;
}
int main()
{
vector<int> a = {1, 2, 3, 4, 6, 7};
// func(a, 5);
func(a, 4);
for (int x : a)
cout << x << ' ';
cout << endl;
return 0;
}
1 2 3 6 4 7
用队列模拟即可 时间复杂度: O ( p ) O(p) O(p)、空间复杂度: O ( n ) O(n) O(n)
#include <iostream>
#include <queue>
using namespace std;
int main()
{
int n, p;
cin >> n >> p;
queue<int> q;
for (int i = 0; i < n; i ++)
{
int x;
cin >> x;
q.push(x);
}
while (p --)
{
int x = q.front();
q.pop();
q.push(x);
}
while (q.size())
{
cout << q.front() << ' ';
q.pop();
}
cout << endl;
return 0;
}
6 3
1 2 3 4 5 6
4 5 6 1 2 3
从小到大遍历,遍历 n n n次得到答案。 n n n为单个数组的长度。
#include <iostream>
using namespace std;
int func(int a[], int b[], int n)
{
int i = 0, j = 0, cnt = 0;
while (i < n && j < n)
{
cnt ++;
if (cnt == n) return min(a[i], b[j]);
if (a[i] <= b[j]) i ++;
else j ++;
}
return -1;
}
int main()
{
int a[] = {11, 13, 15, 17, 19};
int b[] = {2, 4, 6, 8, 20};
int n = 5;
cout << func(a, b, n) << endl;
return 0;
}
11
class Solution {
public:
int moreThanHalfNum_Solution(vector<int>& nums) {
unordered_map<int, int> cnt;
for (int x : nums)
{
cnt[x] ++;
if (cnt[x] > nums.size() >> 1)
return x;
}
return -1;
}
};
[1,2,1,1,3]
1
算法设计思路:
-
遍历数组,统计1~n之间每个整数是否出现过。可以使用一个bool数组标记,长度为n,默认全部为false。
-
再次遍历原数组,将出现的整数的位置设为true。
-
最后遍历bool数组,找出第一个位置为false的整数,即为未出现的最小正整数。
C++代码:
#include <iostream>
#include <vector>
using namespace std;
int getFirstMissingPositive(vector<int> &nums)
{
int n = nums.size();
bool mark[n + 1];
// 初始化bool数组
for (int i = 0; i <= n; i++)
mark[i] = false;
// 标记数组中的整数
for (int i = 0; i < n; i++) {
if (nums[i] > 0 && nums[i] <= n)
mark[nums[i]] = true;
}
// 找到第一个未被标记的位置
for (int i = 1; i <= n; i++) {
if (!mark[i])
return i;
}
return n + 1;
}
int main()
{
vector<int> nums = {-5, 3, 2, 3};
cout << getFirstMissingPositive(nums) << endl;
return 0;
}
时间复杂度:O(n) ,只需要遍历数组两次
空间复杂度:O(n) ,使用了一个bool数组标记
该算法利用了撇定负数的特点,将问题转换为查找一个bool数组中的第一个false。时间和空间复杂度都较优。
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 100010;
int a[N], b[N], c[N];
int main()
{
int l, m, n;
cin >> l >> m >> n;
for (int i = 0; i < l; i ++) cin >> a[i];
for (int i = 0; i < m; i ++) cin >> b[i];
for (int i = 0; i < n; i ++) cin >> c[i];
LL res = 1e18;
int i = 0, j = 0, k = 0;
while (i < l && j < m && k < n)
{
int x = a[i], y = b[j], z = c[k];
res = min(res, (LL)max(max(x, y), z) - min(min(x, y), z));
if (x <= y && x <= z) i ++;
else if (y <= x && y <= z) j ++;
else k ++;
}
cout << res * 2 << endl;
return 0;
}
3 4 5
-1 0 9
-25 -10 10 11
2 9 17 30 41
2
单链表
1.设计一个递归算法,删除不带头结点的单链表工中所有值为的结点。
这里是一个递归算法来删除单链表中所有值为x的节点:
#include <iostream>
using namespace std;
struct Node
{
int val;
Node *next;
Node(int x) : val(x), next(NULL) {}
};
// head是单链表的第一个节点
Node *removeAllXNodes(Node* head, int x)
{
if (head == nullptr) return nullptr;
// 如果头节点就是需要删除的节点
if (head->val == x)
return removeAllXNodes(head->next, x);
head->next = removeAllXNodes(head->next, x);
return head;
}
Node *createLinkedList(int vals[], int n)
{
Node *dummy = new Node(0);
Node *cur = dummy;
for (int i = 0; i < n; i++) {
cur->next = new Node(vals[i]);
cur = cur->next;
}
return dummy->next;
}
void print(Node* head)
{
for (Node* p = head; p; p = p->next)
cout << p->val << ' ';
cout << endl;
}
int main()
{
int a[] = {1, 2, 3, 2, 3, 5, 7, 2}, n = sizeof(a) / sizeof(int);
Node *head = createLinkedList(a, n);
print(head);
print(removeAllXNodes(head, 2));
return 0;
}
1 2 3 2 3 5 7 2
1 3 3 5 7
算法步骤:
-
如果头节点为空,直接返回null
-
如果头节点的值是需要删除的x:
- 删除该节点(不再返回该节点)
- 直接递归处理后续节点
-
如果头节点的值不是x:
- 递归处理后继节点,删除后继链表中的x节点
- 返回当前头节点head
-
最终返回的头节点指向删除后链表的第一个节点
此算法一次递归过整个链表,删除所有值为x的节点。时间复杂度O(N)。
迭代写法:
Node* removeAllXNodes(Node* head, int x)
{
Node *pre = new Node(0), *cur = head;
pre->next = cur;
while (cur)
{
if (cur->val == x) // 找到需要删除的节点
{
pre->next = cur->next;
cur = cur->next;
}
else
{
pre = pre->next;
cur = cur->next;
}
}
return head;
}
3.设L为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值。
对于带头结点的单链表,我们可以使用递归的方式从尾到头输出每个节点的值:
void reversePrint(Node* head) {
if (!head) return;
reversePrint(head->next);
cout << head->next->value << " ";
}
算法步骤:
- 递归调用函数处理链表的尾部节点
- 当递归到链表尾部节点时(next为空),结束递归调用返回
- 从返回路径开始,输出每个节点的值
这样实现的时间复杂度为O(N),空间复杂度为O(N)的栈空间。
我们也可以使用迭代的方式实现:
void reversePrint(Node* head) {
stack<int> st;
Node* cur = head->next;
while(cur != nullptr) {
st.push(cur->value);
cur = cur->next;
}
while(!st.empty()) {
cout << st.top() << " ";
st.pop();
}
}
利用栈来模拟递归调用的返回路径,在遍历完链表后,从栈顶依次输出每个节点的值。
两种方法都可以实现带头结点单链表从尾到头反向输出节点的值功能。
4. 试编写在带头结点的单链表 L 中删除一个最小值结点的高效算法(假设最小值结点是唯一的)。
下面是在带头结点的单链表中删除一个最小值结点的高效算法:
Node* deleteMinNode(Node* dummy)
{
Node* prevMin = dummy;
Node* min = dummy->next;
for (Node* cur = dummy; cur->next; cur = cur->next)
{
if (cur->next->val < min->val)
{
prevMin = cur;
min = cur->next;
}
}
// 删除最小结点
prevMin->next = min->next;
delete min;
return dummy;
}
算法步骤:
-
初始化cur指针从头节点开始遍历链表
-
初始化prevMin和min分别指向头节点和第一个节点
-
每遍历一个节点,检查其值是否小于当前最小值候选结点min
- 如果小于,更新prevMin和min指针
-
遍历结束后,prevMin和min分别指向最小值结点的前驱和结点本身
-
删除min结点,即断开prevMin->next链接
-
返回链表头节点
这个算法一次遍历就可以找到最小值结点和其前驱节点。
之后直接删除即可,时间复杂度为O(N),空间复杂度为O(1)。
它利用了链表特点,以线性时间找到最小值结点实现高效删除。
试编写算法将带头结点的单链表就地逆置,所谓“就地”是指辅助空间复杂度为 O(1).
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *pre = NULL, *cur = head;
while (cur)
{
ListNode *n = cur->next;
cur->next = pre;
pre = cur;
cur = n;
}
return pre;
}
};
作者:动力春风
链接:https://www.acwing.com/activity/content/code/content/4022910/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if (!head || !head->next) return head;
ListNode *newHead = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return newHead;
}
};
6.有一个带头结点的单链表 L,设计一个算法使其元素递增有序
写不写头节点无所谓,重点在逻辑,考试的时候也是如此,除非考试特别说明是带头节点的。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Node
{
int val;
Node *next;
Node(int x) : val(x), next(NULL) {}
};
Node *createLinkedList(int vals[], int n)
{
Node *dummy = new Node(0);
Node *cur = dummy;
for (int i = 0; i < n; i++) {
cur->next = new Node(vals[i]);
cur = cur->next;
}
return dummy->next;
}
Node *sortList(Node *head)
{
vector<int> a;
for (Node* p = head; p; p = p->next)
a.push_back(p->val);
sort(a.begin(), a.end());
Node *dummy = new Node(0);
Node *cur = dummy;
for (int x : a)
{
cur->next = new Node(x);
cur = cur->next;
}
return dummy->next;
}
void print(Node* head)
{
for (Node* p = head; p; p = p->next)
cout << p->val << ' ';
cout << endl;
}
int main()
{
int a[] = {1, 2, 3, 2, 0, 3, 5, 7, 2}, n = sizeof(a) / sizeof(int);
Node *head = createLinkedList(a, n);
print(head);
print(sortList(head));
return 0;
}
1 2 3 2 0 3 5 7 2
0 1 2 2 2 3 3 5 7
7.设在一个带表头结点的单链表中所有元素结点的数据值无序,试编写一个函数,删除表中所有介于给定的两个值 (作为函数参数给出)之间的元素的元素 (若存在)。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Node
{
int val;
Node *next;
Node(int x) : val(x), next(NULL) {}
};
Node *createLinkedList(int vals[], int n)
{
Node *dummy = new Node(0);
Node *cur = dummy;
for (int i = 0; i < n; i++) {
cur->next = new Node(vals[i]);
cur = cur->next;
}
return dummy->next;
}
Node *del(Node* dummy, int l, int r)
{
Node *pre = dummy, *cur = dummy->next;
while (cur)
{
if (cur->val >= l && cur->val <= r) // 找到需要删除的元素
{
pre->next = cur->next;
cur = cur->next;
}
else
{
pre = pre->next;
cur = cur->next;
}
}
return dummy->next;
}
void print(Node* head)
{
for (Node* p = head; p; p = p->next)
cout << p->val << ' ';
cout << endl;
}
int main()
{
int a[] = {1, 2, 3, 2, 0, 3, 5, 7, 2}, n = sizeof(a) / sizeof(int);
Node *head = createLinkedList(a, n);
print(head);
Node *dummy = new Node(0);
dummy->next = head;
print(del(dummy, 2, 4));
return 0;
}
1 2 3 2 0 3 5 7 2
1 0 5 7
8.给定两个单链表,编写算法找出两个链表的公共结点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *findFirstCommonNode(ListNode *headA, ListNode *headB) {
ListNode *a = headA, *b = headB;
while (a != b)
{
if (a) a = a->next;
else a = headB;
if (b) b = b->next;
else b = headA;
}
return a;
}
};
作者:动力春风
链接:https://www.acwing.com/activity/content/code/content/4023098/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
9. 给定一个带表头结点的单链表,设 head 为头指针,结点结构为(data,next),data为整型元素,next 为指针,试写出算法: 按递增次序输出单链表中各结点的数据元素 (要求: 不允许使用数组作为辅助空间)。
#include <iostream>
using namespace std;
struct Node
{
int val;
Node *next;
Node(int x) : val(x), next(NULL) {}
};
void Min_Delete(Node *dummy)
{
while (dummy->next)
{
Node *pre = dummy, *cur = dummy->next;
while (cur->next)
{
if (cur->next->val < pre->next->val)
pre = cur;
cur = cur->next;
}
cout << pre->next->val << ' ';
Node *p = pre->next;
pre->next = p->next;
delete p;
}
}
Node *createLinkedList(int vals[], int n)
{
Node *dummy = new Node(0);
Node *cur = dummy;
for (int i = 0; i < n; i++) {
cur->next = new Node(vals[i]);
cur = cur->next;
}
return dummy->next;
}
void print(Node* head)
{
for (Node* p = head; p; p = p->next)
cout << p->val << ' ';
cout << endl;
}
int main()
{
int a[] = {1, 2, 3, 2, 2, 3, 5, 7, 2}, n = sizeof(a) / sizeof(int);
Node *head = createLinkedList(a, n);
print(head);
Node *dummy = new Node(0);
dummy->next = head;
Min_Delete(dummy);
return 0;
}
1 2 3 2 2 3 5 7 2
1 2 2 2 2 3 3 5 7