写在前面:这本书不是圣经,一定要自己理解思路后独立完成代码

链表

【面试题6】:从尾到头打印链表

题目:输入一个链表的头结点,从尾巴到头反过来打印出每个节点的值。

#include <iostream>
#include <stack>
using namespace std;
//链表节点
struct ListNode {
	int m_nValue;	//数据域
	ListNode* m_pNext;//指针域
};

//往链表结尾插入一个节点
void insert_to_tail(ListNode* &head, int x) {//由于要改变头结点,所以这里是传引用,比原来的传双重指针更好理解
	ListNode* pNew = new ListNode;
	pNew->m_nValue = x;
	pNew->m_pNext = NULL;
	if (head == NULL) {
		head = pNew;
	}
	else {
		ListNode* p = head;//由于是传引用,所以要避免直接修改head,故创建临时变量p
		while (p->m_pNext != NULL) {
			p = p->m_pNext;
		}
		p->m_pNext = pNew;
	}
}
//display
void display(ListNode* head) {
	while (head!= NULL) {
		cout << head->m_nValue<<" ";
		head = head->m_pNext;
	}
	cout << endl;
}
//从尾到头打印链表
void print_tail_to_head(ListNode* head) {
	stack<ListNode*>nodes;
	while (head != NULL) {
		nodes.push(head);
		head = head->m_pNext;
	}
	while (!nodes.empty()) {
		cout << nodes.top()->m_nValue << " ";
		nodes.pop();
	}
}

int main() {
	ListNode* head = NULL;//创建头节点
	insert_to_tail(head, 1);
	insert_to_tail(head, 2);
	insert_to_tail(head, 3);
	insert_to_tail(head, 4);
	insert_to_tail(head, 5);
	print_tail_to_head(head);
	return 0;
}

【面试题18】:删除链表的节点

题目一:在O(1)时间内删除链表的节点
给定单向链表的头指针和下一个节点指针,定义一个函数在O(1)时间内删除该节点。链表节点与函数的定义如下。
常规做法:从链表的头结点开始遍历,顺序遍历查找要删除的节点,并在链表中删除该节点。复杂度O(n)

void RemoveNode(ListNode* &head, ListNode* p_deleted) {
	if (head == NULL || p_deleted==NULL)
		return;
	ListNode* p = head;
	if (p == p_deleted) {
		head = head->m_pNext;
		delete p;
	}
	else {
		while (p->m_pNext != NULL && p->m_pNext != p_deleted) {
			p = p->m_pNext;
		}
		if (p->m_pNext != NULL && p->m_pNext == p_deleted) {
			p->m_pNext = p->m_pNext->m_pNext;
			delete p_deleted;
		}
	}
}

令一种做法:

//假设p_deleted 是链表中的节点
void RemoveNode(ListNode* &head, ListNode* p_deleted) {
	if (head == NULL || p_deleted==NULL)
		return;
	//不是尾节点(说明至少有两个节点)
	if (p_deleted->m_pNext != NULL) {
		ListNode* p = p_deleted->m_pNext;
		p_deleted->m_nValue = p_deleted->m_pNext->m_nValue;
		p_deleted->m_pNext = p_deleted->m_pNext->m_pNext;
		delete p;
	}
	else if (p_deleted == head) {//是尾节点还是头结点
		head = NULL;
		delete p_deleted;
	}
	else //是尾节点,不是头结点
	{
		ListNode*p = head;
		while (p->m_pNext != p_deleted)
			p = p->m_pNext;
		p->m_pNext = NULL;
		delete p_deleted;
	}
}

题目二:删除链表中重复的节点

void delete_duplication(ListNode* &head) {
	if (head == NULL)
		return;
	ListNode*p = head;
	ListNode* pre = NULL;
	ListNode* pcur = p;
	while (pcur != NULL) {
		ListNode* pnext = pcur->m_pNext;
		bool need_deleted = false;
		if (pnext != NULL && pcur->m_nValue == pnext->m_nValue) {
			need_deleted = true;
		}
		if (!need_deleted) {
			pre = pcur;
			pcur = pcur->m_pNext;
		}
		else {
			int value = pcur->m_nValue;
			ListNode* p_deleted = pcur;;
			while (p_deleted!=NULL && p_deleted->m_nValue==value) {
				pcur = pcur->m_pNext;
				delete p_deleted;
				p_deleted = pcur;
			}
			if (pre == NULL)
				head = pcur;
			else
				pre->m_pNext = pcur;
		}
	}
}

题目三:去重

void delete_duplication_2(ListNode* &head) {
	ListNode*p = head;
	ListNode*pre = NULL;
	ListNode*pcur = p;
	while (pcur!=NULL) {
		ListNode*pnext = pcur->m_pNext;
		bool need_deleted = false;
		if (pnext != NULL && pcur->m_nValue == pnext->m_nValue) {
			need_deleted = true;
		}
		if (!need_deleted) {
			pre = pcur;
			pcur = pcur->m_pNext;
		}
		else {
			ListNode*p_delete = pcur;
			pcur = pnext;
			delete p_delete;
			if (pre == NULL)
				head = pcur;
			else
				pre->m_pNext = pcur;
		}
	}
}

【面试题22】:链表中倒数第K个节点

ListNode* FindKthToTail(ListNode*head, unsigned int k) {
	if (head == NULL || k == 0)
		return nullptr;
	ListNode* p1 = head;
	ListNode* p2 = p1;
	for (unsigned int i = 1; i < k; i++) {
		p2 = p2->m_pNext;
		if (p2 == NULL)
			return NULL;
	}
	while (p2->m_pNext!= NULL) {
		p1 = p1->m_pNext;
		p2 = p2->m_pNext;
	}
	return p1;
}

【面试题24】:反转链表

void reserve(ListNode* &head) {//由于要修改头结点,故传引用
	if (head == NULL)
		return;
	ListNode* pre = NULL;
	ListNode* pcur = head;
	ListNode* pnext = pcur->m_pNext;
	while (pcur!= NULL) {
		pcur->m_pNext = pre;
		pre = pcur;
		pcur = pnext;
		if(pnext!=NULL)
			pnext = pnext->m_pNext;
	}
	head = pre;//注意退出循环时的条件,此时pre才是指向最后一个元素
}

【面试题25】:合并两个排序的链表

ListNode* merge(ListNode* head1, ListNode* head2) {
	if (head1 == NULL)
		return head2;
	if (head2 == NULL)
		return head1;
	ListNode* merge_head = NULL;
	if (head1->m_nValue < head2->m_nValue) {
		merge_head = head1;
		merge_head->m_pNext = merge(head1->m_pNext, head2);
	}
	else {
		merge_head = head2;
		merge_head->m_pNext = merge(head1, head2->m_pNext);
	}
	return merge_head;
}

【面试题62】:圆桌中最后剩下的数字

题目:0,1,2…,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。
经典解法:用环形链表模拟圆圈。

int lastRemaining(unsigned n, unsigned m) {
	list<int>numbers;
	for (int i = 0; i < n; i++)
		numbers.push_back(i);

	list<int>::iterator cur = numbers.begin();
	while (numbers.size() > 1) {
		for (int i = 0; i < m - 1; i++) {
			cur++;	//注意end是超尾,所以先增加再判断
			if (cur == numbers.end())
				cur = numbers.begin();
		}
		list<int>::iterator next =++cur;
		if (next == numbers.end())
			next = numbers.begin();
		cur--;
		numbers.erase(cur);//迭代器cur指向的内容被删除了,但迭代器对象本身还在
		cur = next;
	}
	return *cur;
}