第2章链表—2.1~2.5题【倒计时22天】

链表相关的面试问题极其常见,注意单向链表和双向链表的区别,在解答这一章的问题之前,节点的创建、链表的增删查改等操作应该熟记于心。

以单向节点为例,基本的代码实现如下:

class listNode
{
public:
	listNode(int value=0):_value(value){_pNext = NULL;}
	~listNode(){_pNext = NULL;}
	//int data(){return this->_value;}
	//listNode* next(){return this->_pNext;}
	void appendToTail(int value){
		listNode* end = new listNode(value);
		listNode* p = this;
		while(p->_pNext!=NULL)
			p = p->_pNext;
		p->_pNext = end;
	}

public: 
	int _value;
	listNode* _pNext;
};

/*
@ delete a listNode with value=d, return head of listNode.
*/
listNode* deleteListNode(listNode* head, int d)
{
	if(head==NULL)
		return NULL;
	listNode* pNode = head;
	if(head->_value == d){//if it occurs in head
		pNode = pNode->_pNext;
		delete head;
		head = NULL;
		return pNode;
	}

	while(pNode->_pNext){
		if(pNode->_pNext->_value==d){
			listNode* pTmpNode = pNode->_pNext;
			pNode->_pNext = pNode->_pNext->_pNext;
			delete pTmpNode; 
			pTmpNode = NULL;
			return head;
		}
		pNode = pNode->_pNext;
	}
	return head;//if NOT found
}

/*
@ delete Node from list
@ Make sure to handle the case if pNode is the last node in the linked list 
*/
bool deleteListNode(listNode* pNode)
{
	if(pNode==NULL || pNode->_pNext == NULL)
		return false;
	listNode* pNext = pNode->_pNext;
	pNode->_value = pNext->_value;
	pNode->_pNext = pNext->_pNext;
	delete pNext;
	return true;
}

void displayList(listNode* head)
{
	if(head==NULL)
		cout << "Invalid list." << endl;
	listNode* pNode = head;
	while(pNode){
		cout << setw(5) << pNode->_value;
		pNode = pNode->_pNext;
	}
	cout << endl;
}


2.1. 删除一个无序链表中的重复节点,如果不允许使用辅助buffer如何实现?

方法1.借助辅助buffer和hashTable,假设节点中的数据为整型且在[0,256)之间

/*
@ 删除列表中的重复节点
@ 假定节点的数据为int,且取值为【0,256】
*/
void deleteDups(listNode* head)
{
	if(head==NULL)
		return;
	bool hashTable[256] = {false};
	listNode* pNode = head;
	hashTable[pNode->_value%256] = true;
	while(pNode->_pNext){
		if(hashTable[pNode->_pNext->_value%256]==false){
			hashTable[pNode->_pNext->_value%256] = true;
			pNode = pNode->_pNext;
		}
		else{
			listNode* pNodeTmp = pNode->_pNext;
			pNode->_pNext = pNode->_pNext->_pNext;
			delete pNodeTmp;
			pNodeTmp = NULL;
		}			
	}
}
方法2. 使用两个指针进行迭代比较,current从头到尾遍历链表,previous指向以处理完链表片段的末尾节点。对于每一个current指向的节点,使用runner遍历[head, previous]以检测是否有重复的节点。

void deleteDups2(listNode* head)
{
	if(head == NULL)
		return;
	listNode* previous = head;
	listNode* current = head->_pNext;
	while(current!=NULL)
	{
		listNode* Runner = head;
		while(Runner != current){//在[head, previous]区间内寻找和current相等的节点
			if(Runner->_value == current->_value){//如果存在雷同节点
				listNode* pTmp = current;//则删除current节点
				previous->_pNext = current->_pNext;//已处理完区间的下一个节点位置后移
				current = current->_pNext;
				delete pTmp; pTmp = 0;
				break;
			}
			Runner = Runner->_pNext;
		}
		if(Runner==current){//遍历完后并未发现相同节点,则将current节点移入已处理节点
			previous = current;
			current = current->_pNext;//往后遍历节点
		}
	}
}

2.2. 查找距离单向链表最后节点距离为n的节点

方法1. 首先确定首尾节点距离length,然后再次首节点出发后移length-n个节点即可。最坏情况下,需要遍历链表两次。(代码略)

方法2. 双指针迭代法,1次遍历即可。步骤如下:1)创建p1,p2均指向head;2) 向后移动p2,使得p1和p2间隔n个节点;3) 同步递增p1和p2,直到p2->next==NULL,返回p1。代码如下:

void deleteDups2(listNode* head)
{
	if(head == NULL)
		return;
	listNode* previous = head;
	listNode* current = head->_pNext;
	while(current!=NULL)
	{
		listNode* Runner = head;
		while(Runner != current){//在[head, previous]区间内寻找和current相等的节点
			if(Runner->_value == current->_value){//如果存在雷同节点
				listNode* pTmp = current;//则删除current节点
				previous->_pNext = current->_pNext;//已处理完区间的下一个节点位置后移
				current = current->_pNext;
				delete pTmp; pTmp = 0;
				break;
			}
			Runner = Runner->_pNext;
		}
		if(Runner==current){//遍历完后并未发现相同节点,则将current节点移入已处理节点
			previous = current;
			current = current->_pNext;//往后遍历节点
		}
	}
}

2.3. 删除单链表中的某个节点,有一个约束,能且仅能操作链表的待删除节点。

有一点需要注意的是,如果待删除节点位于链表的最后一个节点,如何处理?删除最后一个节点会导致指针悬空,这种情况下需要特除处理,要么不删除,要不将该节点的数据置为无效。代码实现见链表的实现。


2.4. 两个单链表表征的数,每个数字由一个节点表示,且低位在前、高位在后,如果315表示为(3->1->5)。编码实现两个数的相加操作,如(3->1->5) + (5->9->2) = (8->0->8)。

思路:递归实现

/*
@ recursion method to add lists
*/
listNode* addLists(listNode* l1, listNode* l2, int carry)
{
	if(l1==NULL || l2==NULL)
		return NULL;
	int value = carry;
	if(l1!=NULL)
		value += l1->_value;
	if(l2!=NULL)
		value += l2->_value;
	listNode* pNode = new listNode(value%10);
	listNode* pMore = addLists(l1->_pNext, l2->_pNext, value/10);
	pNode->_pNext = pMore;
	return pNode;
}

2.5. 找出环形单向链表中环的起始位置。如有单向链表 A->B->C->D->E->F->C,输出为C。

思路:两倍速追赶法

listNode* findBeginning(listNode* head)
{
	if(head==NULL || head->_pNext==NULL || head->_pNext->_pNext==NULL)
		return NULL;
	listNode* p1 = head;
	listNode* p2 = head;
	while(p2=p2->_pNext->_pNext){//找到相遇点
		p1=p1->_pNext;
		if(p1==p2)
			break;
	}
	if(p2==NULL)//如果没有相遇点,则表明不存在环
		return NULL;
	p1 = head;//将p1移到头部,p2保持在相遇点
	while(p1!=p2){//同步移动p1和p2,直到他们相遇,此时的相遇点为环的起始位置
		p1 = p1->_pNext;
		p2 = p2->_pNext;
	}
	return p2;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值