单链表中的常见算法问题(剑指offer5/13/15/16/17)

本文介绍了单链表的基本操作,包括创建、删除、遍历输出、获取长度、逆置、O(1)时间删除指定节点、查找倒数第K个节点的值及合并两个排序链表的方法。

1.单链表的结点

struct ListNode {
	int m_nKey;
	ListNode* m_pNext;
};

2.创建链表

ListNode* createList(int length) {
	//创建表头结点
	ListNode* head = new ListNode();
	head->m_pNext = NULL;
	ListNode* p = head;

	int value = 0; //临时存储用户键入的第i个结点值
	for (int i = 0; i < length; ++i) {
		cout << "ListNode" << i << ":\t";
		cin >> value;

		//将新创建的结点插入链表中
		ListNode* q = new ListNode();
		q->m_nKey = value;
		q->m_pNext = NULL;
		p->m_pNext = q;
		p = q;
	}

	return head;
}

3.删除链表

void deleteList(ListNode* head) {
	if (NULL == head) {
		return;
	}

	ListNode* p = head;
	ListNode* q = p;
	while (p->m_pNext) {
		q = p;
		p = p->m_pNext;

		delete q;
		q = NULL;
	}

	delete head;
	head = NULL;
}

4.遍历输出链表(从表头到表尾)

void showList(ListNode* head) {
	//如果是空链表直接退出
	if (NULL == head)
		return;

	ListNode* p = head->m_pNext;
	while (p != NULL) {
		cout << p->m_nKey << "\t";
		p = p->m_pNext;
	}

	cout << endl;
}

5.链表的长度

int listLength(ListNode* head) {
	if (NULL == head) {
		return 0;
	}

	int length = 0;
	ListNode*p = head->m_pNext;
	while (NULL != p) {
		p = p->m_pNext;
		++length;
	}
	return length;
}

6.链表逆置

ListNode* listReverse(ListNode* sourceHead) {
	if (NULL == sourceHead)
		return NULL;

	ListNode* newHead = new ListNode();
	newHead->m_pNext = NULL;

	ListNode* p = NULL; //中间结点

	while (sourceHead->m_pNext != NULL) {
		//将原来链表的第一个结点从链表中删除
		p = sourceHead->m_pNext;
		sourceHead->m_pNext = p->m_pNext;

		//将删除掉的结点以头插法插入到新的链表中
		p->m_pNext = newHead->m_pNext;
		newHead->m_pNext = p;
	}

	return newHead;
}

7.O(1)时间删除一个指定结点

void deleteListNode(ListNode* pHeadListNode, ListNode* pToBeDeleted) {
	if (NULL == pHeadListNode || NULL == pToBeDeleted) {
		return;
	}

	//方法二:在保证链表中包含待删除结点的前提下,将待删除结点的下一个结点值
	//替换掉要删除结点的值,然后删除待删除结点的下一个结点

	//如果待删除结点是链表中的唯一结点
	ListNode* pHeadNext = pHeadListNode->m_pNext;
	if (pHeadNext == pToBeDeleted && pHeadNext ==
	NULL) {
		delete pToBeDeleted;
		pToBeDeleted = NULL;
		pHeadNext = NULL;
	}
	//如果要删除的结点不是最后一个结点,且待删除的链表的长度大于1
	else if (pToBeDeleted->m_pNext != NULL) {
		ListNode* pDeletedNext = pToBeDeleted->m_pNext;
		pToBeDeleted->m_nKey = pDeletedNext->m_nKey;
		pToBeDeleted = pDeletedNext->m_pNext;

		delete pDeletedNext;
	}
	//如果待删除结点是链表的末尾结点(也是最常见的操作方法)
	else {
		//方法一:找到要删除结点,用其前一个结点指向其下一个结点,从而达到删除的目的
		ListNode* p = pHeadListNode;
		ListNode* q = pHeadListNode->m_pNext;
		while (q != pToBeDeleted && q != NULL) {
			p = q;
			q = q->m_pNext;
		}
		if (q != NULL) {
			p->m_pNext = q->m_pNext;
		}
	}
}

8.输出单链表中的倒数第K个结点的值

int findLastKNodeValue(ListNode* head, const unsigned int k) {
	if (NULL == head || k <= 0) {
		return -1;
	}

	//计数器
	unsigned int count = 0;
	ListNode* p = head;
	ListNode* q = head;

	//如果链表长度多于k,则q先遍历k个值
	while (count < k && q != NULL) {
		q = q->m_pNext;
		++count;
	}

	//如果少于k直接退出
	if (NULL == q) {
		return -1;
	}

	//如果链表的结点多于k个,两个指针一起遍历到先遍历指针为NULL时
	while (NULL != q) {
		q = q->m_pNext;
		p = p->m_pNext;
	}

	//当链表长度多于k时,输出倒数第k个结点的值
	return p->m_nKey;
}

9.合并两个排序的链表

ListNode* mergeSortedLinkedList(ListNode* head1, ListNode* head2) {
	//有个链表为空时返回另外一个链表即可--代码的鲁棒性
	if (NULL == head1) {
		return head2;
	}

	if (NULL == head2) {
		return head1;
	}

	//合并后的新头 结点
	ListNode* head = new ListNode();
	head->m_pNext = NULL;

	ListNode* s = head;
	while (NULL != head1->m_pNext && NULL != head2->m_pNext) {
		ListNode* p = head1->m_pNext;
		ListNode* q = head2->m_pNext;

		if (p->m_nKey < q->m_nKey) {
			head1->m_pNext = p->m_pNext;

			p->m_pNext = s->m_pNext;
			s->m_pNext = p;
			s = p;
		} else {
			head2->m_pNext = q->m_pNext;

			q->m_pNext = s->m_pNext;
			s->m_pNext = q;
			s = q;
		}

	}

	while (NULL != head1->m_pNext) {
		ListNode* p = head1->m_pNext;
		head1->m_pNext = p->m_pNext;

		p->m_pNext = s->m_pNext;
		s->m_pNext = p;
		s = p;
	}

	while (NULL != head2->m_pNext) {
		ListNode* q = head2->m_pNext;
		head2->m_pNext = q->m_pNext;

		q->m_pNext = s->m_pNext;
		s->m_pNext = q;
		s = q;
	}

	return head;
}

测试代码

/*
 *
 *  Created on: 2014-3-20 16:16:52
 *      Author: danDingCongRong
 */

#include<iostream>
using namespace std;

int main() {
	int listLen = 0;
	cout << "输入链表的长度:\t";
	cin >> listLen;

	ListNode* head = createList(listLen);
	cout << "原始链表:" << endl;
	showList(head);

	cout << "链表的长度为:" << listLength(head) << endl;

	ListNode* newHead = listReverse(head);
	cout << "逆置后的链表:" << endl;
	cout << "" << endl;
	showList(newHead);

	cout << "倒数第5个数是:" << endl;
	cout << findLastKNodeValue(newHead, 5) << endl;

	ListNode* head1 = createList(listLen);
	cout << "原始链表:" << endl;
	showList(head1);

	ListNode* head2 = createList(listLen);
	cout << "原始链表:" << endl;
	showList(head2);

	cout << "合并两个有序的链表:" << endl;
	ListNode* head3 = mergeSortedLinkedList(head1, head2);
	showList(head3);

	cout << "链表的长度为:" << listLength(head3) << endl;

	return 0;
}


注:部分代码参考自剑指offer




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值