目录
本系列面试题来自《剑指offer》第二版,以c/c++实现,代码在
g++ 11.4.0
均运行通过
面试题1:赋值运算符函数
- 本题考察实现自己的string类
#include <iostream>
#include <string.h>
using namespace std;
class CMyString {
public:
CMyString(char* pData = NULL);
CMyString(const CMyString& str);
CMyString& operator = (const CMyString& str);
~CMyString();
const char* c_str() { return this->m_pData; }
private:
char* m_pData;
};
CMyString::CMyString(char* pData) {
if (pData) {
// 分配空间
int len = strlen(pData);
m_pData = new char[len + 1];
// 拷贝
for (int i = 0; i < len; ++i) {
this->m_pData[i] = pData[i];
}
m_pData[len] = '\0';
}
}
CMyString::CMyString(const CMyString& str) {
if (str.m_pData) {
int len = strlen(str.m_pData);
m_pData = new char[len+1];
for (int i = 0; i < len; ++i) {
this->m_pData[i] = str.m_pData[i];
}
m_pData[len] = '\0';
}
}
CMyString& CMyString::operator = (const CMyString& str) {
// 非自赋值
if (this != &str) {
CMyString tempStr(str);
char* pTemp = tempStr.m_pData;
tempStr.m_pData = m_pData;
m_pData = pTemp;
}
return *this;
}
CMyString::~CMyString() {
if (m_pData) {
delete[] m_pData;
m_pData = nullptr;
}
}
int main() {
CMyString str1;
str1 = "Hello World";
cout << str1.c_str() << endl;
CMyString str2(str1);
cout << str2.c_str() << endl;
CMyString str3 = str1;
cout << str3.c_str() << endl;
return 0;
}
面试题2:实现单例模式
- 饿汉式实现示例如下:
#include <iostream>
#include <thread>
using namespace std;
class Singleton {
private:
Singleton() {}
static Singleton* instance;
public:
static Singleton* getInstance() {
return instance;
}
static void destroyInstance() {
if (instance) {
delete instance;
instance = nullptr;
}
}
};
Singleton* Singleton::instance = new Singleton();
int main() {
for (int i = 0; i < 1000; ++i) {
thread t([](){
Singleton* instance = Singleton::getInstance();
cout << instance << endl;
});
if (t.joinable()) {t.join();}
}
Singleton::destroyInstance();
return 0;
}
面试题3:二维数组中的查找
- 实现如下:
#include <iostream>
using namespace std;
/**
* @desc 在二维数组中查找number,这个二维数组满足每一行递增,每一列递增
*/
bool find(int* matrix, int rows, int columns, int number) {
bool find = false;
if (matrix && rows > 0 && columns > 0) {
int column = columns - 1;
int row = 0;
while (row < rows && column >= 0) {
if (matrix[row*columns + column] == number) {
find = true;
return find;
} else if (matrix[row*columns + column] < number) {
row++;
} else {
column--;
}
}
}
return find;
}
int main() {
int arr[][3] = {1, 2, 4, 5, 6, 7};
cout << find(arr[0], 2, 3, 6) << endl;
cout << find(arr[0], 2, 3, 7) << endl;
cout << find(arr[0], 2, 3, 3) << endl;
cout << find(arr[0], 2, 3, 1) << endl;
return 0;
}
面试题4:替换空格
- 实现如下:
#include <iostream>
#include <string.h>
using namespace std;
/**
* @desc 替换字符串str中的空格,将每一个空格替换成"%20"。假设输入的字符串后面有足够多的空域内存
* @param len 字符串的有效长度
*/
void replaceSpace(char str[], int len) {
if (str && len > 0) {
// 统计空格的个数
int count = 0;
for (int i = 0; i < len; ++i) {
if (str[i] == ' ') {
count++;
}
}
int newLength = len + count * 2;
if (count == 0) return;
str[newLength] = '\0';
for (int i = len - 1, j = newLength - 1; i >= 0; i--) {
if (str[i] == ' ') {
str[j--] = '0';
str[j--] = '2';
str[j--] = '%';
} else {
str[j] = str[i];
j--;
}
}
}
}
int main() {
char str[64] = "test haha dd d hhh";
replaceSpace(str, strlen(str));
cout << str << endl;
return 0;
}
面试题5:从尾到头打印链表
- 不可以更改链表结构,实现如下:
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
#define VALUE_TYPE int
typedef struct SingleLinkList {
VALUE_TYPE value;
SingleLinkList* next;
}Node;
/**
* @desc 头插法创建单链表
*/
Node* createLinkList(const vector<VALUE_TYPE>& vec) {
Node* head = nullptr;
for (auto value : vec) {
Node* newnode = (Node*)malloc(sizeof(Node));
newnode->value = value;
newnode->next = head;
head = newnode;
}
return head;
}
/**
* @desc 逆序遍历单链表
* @param head 单链表的第一个数据节点
*/
void reversePrintLinkList(Node* head) {
if (head) {
Node* cur = head;
stack<VALUE_TYPE> s;
while (cur) {
s.push(cur->value);
cur = cur->next;
}
while (!s.empty()) {
VALUE_TYPE value = s.top();
s.pop();
cout << value << "\t";
}
cout << endl;
}
}
/**
* @desc 正序遍历单链表
*/
void printLinkList(Node* head) {
Node* cur = head;
while (cur) {
cout << cur->value << "\t";
cur = cur->next;
}
cout << endl;
}
/**
* @desc 反转链表:会更改链表结构
*/
void reverseLinkList(Node** node) {
if (!node) return;
Node* pre = nullptr;
Node* cur = *node;
Node* temp;
while (cur) {
temp = cur->next;
cur->next = pre;
pre = cur;
cur = temp;
}
*node = pre;
}
int main() {
Node* node = createLinkList({1, 3, 5, 7});
printLinkList(node);
reversePrintLinkList(node);
reverseLinkList(&node);
printLinkList(node);
node = createLinkList({1024});
printLinkList(node);
reversePrintLinkList(node);
reverseLinkList(&node);
printLinkList(node);
return 0;
}
- 可以更改链表结构,则反转链表再打印
面试题6:重建二叉树
- 需求:根据某个二叉树的前序遍历和中序遍历的结果,重建二叉树
- 实现如下:
#include <iostream>
using namespace std;
#define VALUE_TYPE int
typedef struct BinaryTreeNode {
VALUE_TYPE value;
BinaryTreeNode* left;
BinaryTreeNode* right;
}Node;
Node* create_core(int* startPreorder, int* endPreorder, int* startInorder, int* endInorder) {
// 前序遍历的第一个数字是根节点的值
int rootValue = startPreorder[0];
Node* root = new Node();
root->value = rootValue;
root->left = root->right = nullptr;
// 只有一个节点
if (startPreorder == endPreorder) {
if (startInorder == endInorder && *startInorder == *startPreorder) {
return root;
} else {
throw("Invalid input");
}
}
// 中序遍历中寻找根节点的值
int* rootInorder = startInorder;
while (rootInorder <= endInorder && *rootInorder != rootValue) {
rootInorder++;
}
if (rootInorder > endInorder) {
throw("Invalid input");
}
int leftLength = rootInorder - startInorder;
int* leftPreorderEnd = startPreorder + leftLength;
if (leftLength > 0) {
//构建左子树
root->left = create_core(startPreorder + 1, leftPreorderEnd, startInorder, rootInorder - 1);
}
// 是否剩余的节点足够构建右子树
if (leftLength < endPreorder - startPreorder) {
// 构建右子树
root->right = create_core(leftPreorderEnd + 1, endPreorder, rootInorder + 1, endInorder );
}
return root;
}
/**
* @desc 根据二叉树的前序和中序遍历结果构造二叉树
* @param preorder 先序遍历结果
* @param inorder 中序遍历结果
* @param len 二叉树节点的个数
* @return 构造的二叉树的根节点
*/
Node* create(int* preorder, int* inorder, int len) {
if (!preorder || !inorder || len <= 0) return nullptr;
return create_core(preorder, preorder + len - 1, inorder, inorder + len - 1);
}
/**
* @desc 后序遍历二叉树
*/
void postTraverse(Node* root) {
if (root) {
postTraverse(root->left);
postTraverse(root->right);
cout << root->value << "\t";
}
}
int main() {
// int preorder[] = {1, 2, 4, 7, 3, 5, 6, 8};
// int inorder[] = {4, 7, 2, 1, 5, 3, 8, 6};
// int len = sizeof(preorder) / sizeof(int);
int preorder[] = {};
int inorder[] = {};
int len = sizeof(preorder) / sizeof(int);
cout << len << endl;
Node* root = create(preorder, inorder, len);
postTraverse(root);
cout << endl;
return 0;
}
面试题7:栈和队列的模拟
- 用两个栈实现队列,实现如下:
#include <iostream>
#include <stack>
using namespace std;
template<typename T>
class Queue {
public:
Queue() {}
~Queue() {}
void appendTail(const T& value);
T deleteHead();
private:
stack<T> _in;
stack<T> _out;
};
// 入队
template<typename T>
void Queue<T>::appendTail(const T& value) {
_in.push(value);
}
// 出队
template<typename T>
T Queue<T>::deleteHead() {
while (!_in.empty()) {
_out.push(_in.top());
_in.pop();
}
if (!_out.size()) throw ("Queue is empty\n");
T result = _out.top();
_out.pop();
while (!_out.empty()) {
_in.push(_out.top());
_out.pop();
}
return result;
}
int main() {
Queue<int> queue;
queue.appendTail(10);
queue.appendTail(20);
queue.appendTail(30);
cout << queue.deleteHead() << endl;
cout << queue.deleteHead() << endl;
cout << queue.deleteHead() << endl;
// cout << queue.deleteHead() << endl;
return 0;
}
- 用两个队列实现栈,实现如下:
#include <iostream>
#include <queue>
using namespace std;
template <typename T>
class Stack {
public:
Stack() {}
~Stack() {}
void push(const T& value);
T pop();
private:
queue<T> _push;
queue<T> _pop; // 用于中转
};
// 入栈
template <typename T>
void Stack<T>::push(const T& value) {
_push.push(value);
}
// 出栈
template <typename T>
T Stack<T>::pop() {
int size = _push.size();
for (int i = 0; i < size - 1; ++i) {
_pop.push(_push.front());
_push.pop();
}
if (_push.empty()) {
throw ("the stack is empty");
}
T value = _push.front();
_push.pop();
size = _pop.size();
for (int i = 0; i < size; ++i) {
_push.push(_pop.front());
_pop.pop();
}
return value;
}
int main() {
Stack<int> s;
s.push(1);
s.push(3);
s.push(5);
try {
cout << s.pop() << endl;
cout << s.pop() << endl;
cout << s.pop() << endl;
cout << s.pop() << endl;
} catch(const char* e) {
cout << e << endl;
}
s.push(100);
s.push(200);
cout << s.pop() << endl;
cout << s.pop() << endl;
return 0;
}
面试题8:旋转数组的最小数字
- 旋转数组:将一个递增排序的数组最开始的若干个元素搬到数组的末尾。如
- {3,4,5,1,2}为{1,2,3,4,5}的一个旋转。
- {1,0, 1,1,1}和{1,1,1,0,1}都可以看成数组{0,1,1,1,1}的旋转。
- 实现如下:
#include <iostream>
using namespace std;
int sequence_find(int* numbers, int left, int right) {
int result = numbers[left];
for (int i = left + 1; i <= right; i++) {
if (result > numbers[i]) {
result = numbers[i];
}
}
return result;
}
/**
* @desc 返回旋转数组的最小值
*/
int min(int* numbers, int len) {
if (!numbers || len <= 0) throw("invalid input");
int left = 0, right = len - 1;
int mid = left;
while (numbers[left] >= numbers[right]) {
if (right - left == 1) {
mid = right;
break;
}
mid = (left + right) / 2;
// 下标left/right/mid上的三个数相同,则使用顺序查找
if (numbers[left] == numbers[right] && numbers[mid] == numbers[left]) {
return sequence_find(numbers, left, right);
}
if (numbers[mid] >= numbers[left]) {
left = mid;
} else if (numbers[mid] <= numbers[right]) {
right = mid;
}
}
return numbers[mid];
}
int main() {
// int arr[] = {3, 4, 5, 1, 2};
// int arr[] = {1};
int arr[] = {0, 1, 1, 1, 1};
cout << min(arr, 5) << endl;
return 0;
}
面试题9:斐波那契数列
- 递归写法如下:慢如蜗牛
#include <iostream>
using namespace std;
int calcFibonacci(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
return calcFibonacci(n - 1) + calcFibonacci(n - 2);
}
int main() {
cout << calcFibonacci(10) << endl;
return 0;
}
- 利用动态规划的思想,循环写法如下:
#include <iostream>
#include <assert.h>
using namespace std;
/**
* @desc 循环写法:从F(1)求到F(n)
*/
long calcFibonacci(int n) {
assert(n >= 0);
long result[] = {0, 1};
if (n < 2) return result[n];
long fOne = 0, fTwo = 1, sum = 0;
for (int i = 2; i <= n; ++i) {
sum = fOne + fTwo;
fOne = fTwo;
fTwo = sum;
}
return sum;
}
int main() {
cout << calcFibonacci(0) << endl;
cout << calcFibonacci(100) << endl;
return 0;
}
- 斐波那契数列的应用:一只青蛙一次可以跳上一级台阶,也可以跳上两级。求该青蛙跳上一个n级的台阶总共有多少种跳法?
#include <iostream>
#include <assert.h>
using namespace std;
long calcSolution(int n) {
assert(n >= 0);
long result[] = {0, 1, 2};
if (n < 3) return result[n];
int stepOne = 1, stepTwo = 2;
long count = 0;
for (int i = 3; i <= n; ++i) {
count = stepOne + stepTwo;
stepOne = stepTwo;
stepTwo = count;
}
return count;
}
int main() {
cout << calcSolution(0) << endl;
cout << calcSolution(1) << endl;
cout << calcSolution(2) << endl;
cout << calcSolution(10) << endl;
return 0;
}
面试题10:位运算
1.二进制中1的个数
- 本题考查基本的位运算。位运算包括:
- 按位与
- 按位或
- 按位异或
- 左移:左移右边补0。可以用于遍历二进制表示的整数的每一位
- 右移:对于无符号数值,右移则左边补0;对于有符号数值,右移则左边补1。可以用于遍历二进制表示的整数的每一位
- 输入一个整数,输出该整数二进制表示中1的个数。实现方式1如下:思路1是循环将整数右移和1进行按位与操作,进行统计整数的每一位,这种方式对于负数不适用,因为负数右移左边补1,将会造成死循环。因此考虑对1进行左移操作,和输入的整数进行按位与操作,进行统计。
#include <iostream>
using namespace std;
int count(int number) {
int result = 0;
unsigned int flag = 1;
while (flag) {
if (number & flag) {
result++;
}
flag = flag << 1;
}
return result;
}
int main() {
cout << count(9) << endl; // 2
cout << count(-5) << endl; // 31
return 0;
}
- 实现方式2:一个整数减去1,会将整数的二进制表示中最右边的1变成0。然后与原整数做与运算,会将该整数最右边一个1变成0。一个整数的二进制表示中有多少个1就可以进行多少次这样的操作。
#include <iostream>
using namespace std;
int count(int number) {
int result = 0;
while (number) {
result++;
number = (number - 1) & number;
}
return result;
}
int main() {
cout << count(9) << endl; // 2
cout << count(-5) << endl; // 31
return 0;
}
- 扩展:
- 判断一个整数是不是2的整数次方:只需要判断一个整数的二进制表示中有且只有一位1
- 输入两个整数m和n,计算需要改变m的二进制表示中的多少位才能得到n:求这两个数的异或,然后再统计异或结果中1的位数。
面试题11:数值的整数次方
- 实现函数
double power(double base, int exponent)
,求base的exponent次方。实现如下:
#include <iostream>
using namespace std;
/**
* @desc 判断两个浮点数是否相等:判断他们的差的绝对值是否在一个很小的范围内
*/
bool equal(double left, double right) {
if ((left - right < 0.0000001) && (left - right > -0.0000001) ) {
return true;
} else {
return false;
}
}
/**
* @desc 求base的exponent次方
*/
double power(double base, int exponent) {
if (equal(base, 0.0)) return base;
double result = 1.0;
if (exponent < 0) {
exponent = -exponent;
for (int i = 1; i <= exponent; ++i) {
result *= base;
}
result = 1 / result;
} else {
for (int i = 1; i <= exponent; ++i) {
result *= base;
}
}
return result;
}
int main() {
cout << power(2.2, 0) << endl;
cout << power(2.2, -2) << endl;
cout << power(2.2, 2) << endl;
cout << power(-2.2, 0) << endl;
cout << power(-2.2, -2) << endl;
cout << power(-2.2, 2) << endl;
cout << power(0, 2) << endl;
cout << power(0, 0) << endl;
return 0;
}
面试题12:打印1到最大的n位数
- 简单思路是使用循环遍历,但是如果输入的n非常大,则会溢出。因此考虑使用字符串来模拟数字,实现如下:
#include <iostream>
#include <assert.h>
#include <string.h>
using namespace std;
bool over(char* numbers) {
bool overflow = false;
// 数字加1
numbers[strlen(numbers) - 1]++;
int takeover = 0;
for (int i = strlen(numbers) - 1; i >= 0; --i) {
numbers[i] += takeover;
if (numbers[i] - '0' > 9) {
numbers[i] = '0';
takeover = 1;
} else {
takeover = 0;
}
}
// 最高位产生进位则表示达到了最大数
if (numbers[0] == '0' && takeover == 1) {
overflow = true;
}
return overflow;
}
void printNumber(char* numbers) {
bool start = false;
int count = 0;
while (!start) {
if (numbers[count] != '0') {
start = true;
}
count++;
}
for (int i = count - 1; i < strlen(numbers); ++i) {
cout << numbers[i];
}
cout << "\t";
}
void print1ToN(int n) {
assert(n > 0);
char* numbers = new char[n+1];
memset(numbers, '0', n);
numbers[n] = '\0';
while (!over(numbers)) {
printNumber(numbers);
}
cout << endl;
}
int main() {
// print1ToN(-1);
// print1ToN(0);
print1ToN(5);
return 0;
}
面试题13:给定待删除的单链表节点,在O(1)时间内删除给定的单链表节点
- 顺序查找到待删除链表的前一节点,再删除链表节点。这种方式时间复杂度为O(n)。
- 将待删除链表节点的下一节点的内容复制到待删除节点,移动指针指向,然后删除待删除节点的下一节点。实现如下:
#include <iostream>
#include <vector>
#include <assert.h>
using namespace std;
#define VALUE_TYPE int
typedef struct LinkListNode {
VALUE_TYPE value;
LinkListNode* next;
}Node;
// 头插法创建链表
Node* createLinkList(vector<int>&& vec) {
Node* head = nullptr;
for (auto value : vec) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->next = head;
newNode->value = value;
head = newNode;
}
return head;
}
// 遍历链表
void traverse(Node* head) {
Node* cur = head;
while (cur) {
cout << cur->value << "\t";
cur = cur->next;
}
cout << endl;
}
/**
* @desc 删除指定的节点
*/
void deleteNode(Node** head, Node* toBeDeletedNode) {
if (!head || !toBeDeletedNode) return;
// 要删除指定的节点不是尾节点
if (toBeDeletedNode->next) {
Node* nextNode = toBeDeletedNode->next;
toBeDeletedNode->value = nextNode->value;
toBeDeletedNode->next = nextNode->next;
free(nextNode);
nextNode = nullptr;
}
// 要删除指定的节点是链表唯一的节点
else if (*head == toBeDeletedNode) {
free(toBeDeletedNode);
*head = nullptr;
toBeDeletedNode = nullptr;
}
// 要删除指定的节点不是唯一节点,且是尾节点
else {
Node* cur = *head;
while (cur->next != toBeDeletedNode) {
cur = cur->next;
}
cur->next = nullptr;
free(toBeDeletedNode);
toBeDeletedNode = nullptr;
}
}
/**
* @desc 获取第index个节点,index从0开始
*/
Node* findNode(int index, Node* head) {
assert(index >= 0);
Node* cur = head;
for (int i = 0; i < index && cur; ++i) {
cur = cur->next;
}
return cur;
}
int main() {
// 多个节点的链表删除头节点,尾节点
Node* head = createLinkList({1,3,5,7,9});
traverse(head);
// 删除头节点
deleteNode(&head, findNode(0, head));
traverse(head);
// 删除尾节点
deleteNode(&head, findNode(3, head));
traverse(head);
// 只有一个节点的链表删除唯一节点
head = createLinkList({1024});
deleteNode(&head, findNode(0, head));
traverse(head);
deleteNode(&head, nullptr);
deleteNode(nullptr, findNode(0, head));
return 0;
}
面试题14:调整数组顺序使得奇数位于偶数前面
- 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位位于数组的前半部分,所有偶数位于数组的后半部分。
- 双指针实现如下:
#include <iostream>
#include <vector>
using namespace std;
void printVec(const vector<int>& vec) {
for (auto value : vec) {
cout << value << "\t";
}
cout << endl;
}
/**
* @desc 调整数组次序
*/
void adjust(vector<int>& vec) {
if (!vec.size() || vec.size() == 1) return;
int size = vec.size();
for (int i = 0, j = size - 1; i != j; ) {
// 偶偶
if (vec[i] % 2 == 0 && vec[j] % 2 == 0) {
j--;
}
// 奇偶
else if (vec[i] % 2 == 1 && vec[j] % 2 == 0) {
i++;
j--;
}
// 奇奇
else if (vec[i] % 2 == 1 && vec[j] % 2 == 1) {
i++;
}
// 偶奇则互换
else if (vec[i] % 2 == 0 && vec[j] % 2 == 1) {
int temp = vec[i];
vec[i] = vec[j];
vec[j] = temp;
i++;
j--;
}
}
}
int main() {
vector<int> vec;
vec = {2};
adjust(vec);
printVec(vec);
vec = {1, 3, 5, 2, 4};
adjust(vec);
printVec(vec);
vec = {2, 4, 5, 1, 9, 0};
adjust(vec);
printVec(vec);
vec = {2, 4, 5, 1, 9};
adjust(vec);
printVec(vec);
return 0;
}
- 双指针进阶版本:
- 判断偶数和奇数有更高效的方式
- 按照某种规则进行调整,这个规则与调整数组的次序应当解耦
- 两数字的交换
#include <iostream> #include <vector> using namespace std; void printVec(const vector<int>& vec) { for (auto value : vec) { cout << value << "\t"; } cout << endl; } /** * @desc 调整数组次序 * @ vec 输入的数组 * @ func 调整数组次序的规则 */ void adjust(vector<int>& vec, bool (*func)(int)) { if (!vec.size() || vec.size() == 1 || !func) return; int size = vec.size(); for (int i = 0, j = size - 1; i != j; ) { // 从头开始寻找第一个偶数 while (i != j && func(vec[i])) { i++; } // 从尾开始寻找第一个奇数 while (i != j && !func(vec[j])) { j--; } if (i < j) { vec[i] = vec[i] ^ vec[j]; vec[j] = vec[i] ^ vec[j]; vec[i] = vec[i] ^ vec[j]; } } } /** * @desc 判断是否为奇数 */ bool isOdd(int value) { return (value & 0x1) == 1; } int main() { vector<int> vec; vec = {2}; adjust(vec, isOdd); printVec(vec); vec = {1, 3, 5, 2, 4}; adjust(vec, isOdd); printVec(vec); vec = {2, 4, 5, 1, 9, 0}; adjust(vec, isOdd); printVec(vec); vec = {2, 4, 5, 1, 9}; adjust(vec, isOdd); printVec(vec); return 0; }
面试题15/16/17
- 链表中倒数第k个节点:使用快慢指针
- 反转链表:使用双指针
- 合并两个有序的链表
- 以上实现如下:
#include <iostream>
#include <vector>
using namespace std;
#define VALUE_TYPE int
typedef struct LinkListNode{
VALUE_TYPE value;
LinkListNode* next;
}Node;
typedef struct LinkList {
size_t size; //节点的个数
Node* phead; // 头节点
}LinkList;
/**
* @desc 头插法创建链表
*/
LinkList* create(vector<int>&& vec) {
if (!vec.size()) return nullptr;
// 头指针
LinkList* list = (LinkList*)malloc(sizeof(LinkList));
for (auto value : vec) {
Node* node = (Node*)malloc(sizeof(Node));
node->value = value;
node->next = list->phead;
list->phead = node;
list->size++;
}
return list;
}
/**
* @desc 反转链表
*/
LinkList* reverseLinkList(LinkList** list) {
// 链表为空或者链表只有一个元素
if (!list || !(*list) || !(*list)->phead || (*list)->size == 1) return nullptr;
Node* pre = nullptr;
Node* cur = (*list)->phead; // 第一个数据节点
Node* temp;
while (cur) {
temp = cur->next;
cur->next = pre;
pre = cur;
cur = temp;
}
(*list)->phead = pre; // 更改头指针的值
return *list;
}
/**
* @desc 正序遍历单链表
*/
void printLinkList(LinkList* list) {
if (!list || !list->phead) return;
Node* cur = list->phead;
while (cur) {
cout << cur->value << "\t";
cur = cur->next;
}
cout << endl;
}
/**
* @desc 销毁链表
*/
void destroyLinkList(LinkList** list) {
if (!list || !(*list)) return;
Node* cur = (*list)->phead;
while (cur) {
Node* temp = cur;
cur = cur->next;
free(temp);
}
free(*list);
*list = nullptr;
}
/**
* @desc 链表倒数第k个节点
*/
Node* getReverseOrderNode(LinkList* list, int k) {
if (!list || !list->phead || k < 1 || k > list->size) return nullptr;
Node* fast = list->phead;
Node* slow = list->phead;
// fast指针先走k步
int step = 1;
while (fast && step <= k) {
fast = fast->next;
step++;
}
while (fast) {
fast = fast->next;
slow = slow->next;
}
return slow;
}
/**
* @desc 合并两个有序的链表,使得链表中的节点递增有序。本题链表结构设计不合理故代码量很大
*/
LinkList* combineTwoList(LinkList* left, LinkList* right) {
if ((!left || !left->phead) && (!right || !right->phead)) return nullptr;
LinkList* newList = (LinkList*)malloc(sizeof(LinkList));
Node* flag = nullptr; // 尾节点
Node* rightCur = nullptr;
Node* leftCur = nullptr;
int count = 0;
if (!left || !left->phead) {
rightCur = right->phead;
// 根据right链表中的节点构造newList
while (rightCur) {
Node* node = (Node*)malloc(sizeof(Node));
node->next = nullptr;
node->value = rightCur->value;
count++;
newList->size++;
rightCur = rightCur->next;
if (count == 1) {
newList->phead = node;
flag = node;
continue;
}
flag->next = node;
flag = node;
}
return newList;
}
if (!right || !right->phead) {
leftCur = left->phead;
// 根据left链表中的节点构造newList
while (leftCur) {
Node* node = (Node*)malloc(sizeof(Node));
node->next = nullptr;
node->value = leftCur->value;
count++;
newList->size++;
leftCur = leftCur->next;
if (count == 1) {
newList->phead = node;
flag = node;
continue;
}
flag->next = node;
flag = node;
}
return newList;
}
leftCur = left->phead;
rightCur = right->phead;
while (leftCur && rightCur) {
VALUE_TYPE min;
if (leftCur->value < rightCur->value) {
min = leftCur->value;
leftCur = leftCur->next;
} else {
min = rightCur->value;
rightCur = rightCur->next;
}
count++;
// 尾插法插入节点
Node* node = (Node*)malloc(sizeof(Node));
newList->size++;
node->next = nullptr;
node->value = min;
// 插入第一个数据节点,记录头指针及尾节点的值
if (count == 1) {
newList->phead = node;
flag = node;
continue;
}
flag->next = node;
flag = node;
}
if (!leftCur && !rightCur) return newList;
if (!leftCur && rightCur) {
while (rightCur) {
Node* node = (Node*)malloc(sizeof(Node));
node->next = nullptr;
node->value = rightCur->value;
flag->next = node;
flag = node;
newList->size++;
rightCur = rightCur->next;
}
}
if (leftCur && !rightCur) {
while (leftCur) {
Node* node = (Node*)malloc(sizeof(Node));
node->next = nullptr;
node->value = leftCur->value;
flag->next = node;
flag = node;
newList->size++;
leftCur = leftCur->next;
}
}
return newList;
}
int main() {
LinkList* list = create({});
reverseLinkList(&list);
printLinkList(list);
Node* node = getReverseOrderNode(list, 1);
cout << ((node == nullptr) ? 0 : node->value) << endl;
destroyLinkList(&list);
list = create({100});
reverseLinkList(&list);
printLinkList(list);
node = getReverseOrderNode(list, 1);
cout << ((node == nullptr) ? 0 : node->value) << endl;
destroyLinkList(&list);
list = create({100, 200, 300, 400, 500});
reverseLinkList(&list);
printLinkList(list);
node = getReverseOrderNode(list, 3);
cout << ((node == nullptr) ? 0 : node->value) << endl;
destroyLinkList(&list);
// 两个递增有序的链表
LinkList* left = create({5, 2, 1});
LinkList* right = create({6, 4, 3});
LinkList* newList = combineTwoList(left, right);
printLinkList(newList);
left = create({5, 2, 1, 0, -1});
right = create({6, 4, 3});
newList = combineTwoList(left, right);
printLinkList(newList);
left = create({});
right = create({6, 4, 3});
newList = combineTwoList(left, right);
printLinkList(newList);
left = create({5, 2, 1});
right = create({});
newList = combineTwoList(left, right);
printLinkList(newList);
left = create({});
right = create({});
newList = combineTwoList(left, right);
printLinkList(newList);
destroyLinkList(&left);
destroyLinkList(&right);
destroyLinkList(&newList);
return 0;
}