本系列面试题来自《剑指offer》第二版,以c/c++实现,代码在g++ 11.4.0均运行通过

面试题1:赋值运算符函数

  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:实现单例模式

  1. 饿汉式实现示例如下:
#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:二维数组中的查找

  1. 实现如下:
#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:替换空格

  1. 实现如下:
#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:从尾到头打印链表

  1. 不可以更改链表结构,实现如下:
#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;
}

  1. 可以更改链表结构,则反转链表再打印

面试题6:重建二叉树

  1. 需求:根据某个二叉树的前序遍历和中序遍历的结果,重建二叉树
  2. 实现如下:
#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:栈和队列的模拟

  1. 用两个栈实现队列,实现如下:
#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;
}
  1. 用两个队列实现栈,实现如下:
#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:旋转数组的最小数字

  1. 旋转数组:将一个递增排序的数组最开始的若干个元素搬到数组的末尾。如
    1. {3,4,5,1,2}为{1,2,3,4,5}的一个旋转。
    2. {1,0, 1,1,1}和{1,1,1,0,1}都可以看成数组{0,1,1,1,1}的旋转。
  2. 实现如下:
#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:斐波那契数列

  1. 递归写法如下:慢如蜗牛
#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;
}
  1. 利用动态规划的思想,循环写法如下:
#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;
}
  1. 斐波那契数列的应用:一只青蛙一次可以跳上一级台阶,也可以跳上两级。求该青蛙跳上一个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的个数
  1. 本题考查基本的位运算。位运算包括:
    1. 按位与
    2. 按位或
    3. 按位异或
    4. 左移:左移右边补0。可以用于遍历二进制表示的整数的每一位
    5. 右移:对于无符号数值,右移则左边补0;对于有符号数值,右移则左边补1。可以用于遍历二进制表示的整数的每一位
  2. 输入一个整数,输出该整数二进制表示中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;
}
  1. 实现方式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;
}
  1. 扩展:
    1. 判断一个整数是不是2的整数次方:只需要判断一个整数的二进制表示中有且只有一位1
    2. 输入两个整数m和n,计算需要改变m的二进制表示中的多少位才能得到n:求这两个数的异或,然后再统计异或结果中1的位数。

面试题11:数值的整数次方

  1. 实现函数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位数

  1. 简单思路是使用循环遍历,但是如果输入的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)时间内删除给定的单链表节点

  1. 顺序查找到待删除链表的前一节点,再删除链表节点。这种方式时间复杂度为O(n)。
  2. 将待删除链表节点的下一节点的内容复制到待删除节点,移动指针指向,然后删除待删除节点的下一节点。实现如下:
#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:调整数组顺序使得奇数位于偶数前面

  1. 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位位于数组的前半部分,所有偶数位于数组的后半部分。
  2. 双指针实现如下:
#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;
}
  1. 双指针进阶版本:
    1. 判断偶数和奇数有更高效的方式
    2. 按照某种规则进行调整,这个规则与调整数组的次序应当解耦
    3. 两数字的交换
    #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

  1. 链表中倒数第k个节点:使用快慢指针
  2. 反转链表:使用双指针
  3. 合并两个有序的链表
  4. 以上实现如下:
#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;
}