您的位置:首页 > 理论基础 > 数据结构算法

数据结构和算法经典100题-第7题

2015-04-21 01:36 218 查看
题目要求

微软亚院之编程判断俩个链表是否相交

给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交。

为了简化问题,我们假设俩个链表均不带环。

问题扩展:

1.如果链表可能有环列?

2.如果需要求出俩个链表相交的第一个节点列?

问题分析:

其实本题目是考察链表的相交和环的问题。

一个链表的相交判断中,带环和不带环的判断方法是不一样的。稍后介绍两个都带环链表和都不带环链表的相交的判断方法。

于是此问题就分解为了3个:

如何判断一条链表是否带环?

如何判断两条不带环链表是否相交?

如何判断两条带环的链表是否相交?

如何判断一条链表是否带环?

那么我们先解决如何判断一条链表是否带环的问题。

一般解决判断链表是否有环问题的方法都是设置两个指针p1和p2,p1和p2分别在链表每次移动1步和2步,若最终相遇,则说明链表有环;若p2指向空,则说明无环。

稍微引申一下,如何返还链表的环开始所在的结点。这里涉及到一个公式推导:

假设单链表的总长度为L,头结点到环入口的距离为a,环入口到p1,p2指针相遇的结点距离为x,环的长度为r,p1总共走了s步,则p2走了2s步。另外,p2要追上p1的话,p2至少要在环里面转了一圈多(假设转了n圈加x的距离),得到以下关系:

s = a + x;

2s = a + nr + x;

=>a + x = nr;

=>a = nr - x;

由上式可知:若在头结点和相遇结点分别设一指针,同步(单步)前进,则最后一定相遇在环入口结点.

那么我们看一下代码实现:

node* DoubleListOfCrossing::isCircle(node * head) {

if (NULL == head) {
return NULL;
}

node* p1 = head;
node* p2 = head;

do {
if (NULL != p1->next && NULL != p2->next && NULL != p2->next->next) {
p1 = p1->next;
p2 = p2->next->next;
} else {
return NULL;
}
}while (p1 != p2);

p2 = head;

while (p1 != p2) {
p1 = p1->next;
p2 = p2->next;
}

return p1;
}


如何判断两条不带环链表是否相交?

通常判断两条不带环的链表相交有两种方法:

利用无环单链表相交的冲要条件:两条无环链表的尾结点一定相交。

假设两无环链表分别是L1和L2,把L2头结点接到L1的尾结点,判断L1是否有环。

这里我给出第二种方法的代码实现:

/* 不带环的情况下判断两个链表是否相交 */
bool DoubleListOfCrossing::isCrossingOfNoCircle() {
if (NULL == m_h1 || NULL == m_h2) {
return NULL;
}

/* 把链表2接到链表1上. */
node * tail1 = m_h1;
while (NULL != tail1->next) {
tail1 = tail1->next;
}

tail1->next = m_h2;
return (NULL == isCircle(m_h1)) ? false : true;

}


如何判断两条都带环的链表是否相交?

这里利用一个充分必要条件:若两条都带环的链表相交,则其中任意一条链表的环所处的结点,必然在与其相交的链表上。

这样就将判断两条都带环的链表的相交问题转化为:

求出一条链表的环结点;

求该环结点是否在另一条链表上。

下面我们看一下代码实现:

node* DoubleListOfCrossing::findCircleNode(node * head) {
return isCircle(head);
}

/* 判断一个结点是否在链表上. */
bool DoubleListOfCrossing::isExist(node* head,node * element) {
if (NULL == head) {
return false;
}

node * tmp = head;
while (NULL != tmp->next) {
if (tmp == element) {
return true;
} else {
tmp = tmp->next;
}
}

return false;
}

/* 两个链表都带环的情况下,判断第一个链表的环结点是否在第二个链表上 */
bool DoubleListOfCrossing::isCrossingOfCircle() {
node * cross = findCircleNode(m_h1);
return isExist(m_h2, cross);
}


ok,解决完以上三个问题,我们继续看我们的题目,那么很容易的就将此问题分解成4个步骤:

判断两条链表是否都由环?

若都有环,则调用都有环的判断相交问题的方法;

若都无环,则调用都无环的判断相交问题的方法;

若一条有环,一条无环,则说明此两条链表不可能相交。

那么我们看一下相关的代码实现:

bool DoubleListOfCrossing::isCrossing() {
#define IS_CIRCLE(a) NULL != isCircle((a))
#define NOT_CIRCLE(a) NULL == isCircle((a))

if (NULL == m_h1 && NULL == m_h2) {
cout<<"NULL == m_h1 && NULL == m_h2"<<endl;
return false;
}

bool bothNotCircle = (NOT_CIRCLE(m_h1) && NOT_CIRCLE(m_h2)) ? true : false;
bool bothCircle = (IS_CIRCLE(m_h1) && IS_CIRCLE(m_h2)) ? true : false;

/* 判断两个链表是否都有环,若都没有环 */
if (bothNotCircle) {
return isCrossingOfNoCircle();
}
/* 若都有环 */
if (bothCircle) {
return isCrossingOfCircle();
}

/* 若一个有环,一个没有环则两个链表不可能相交 */
return false;
}


为了验证这个问题,我写了一个测试程序,代码如下:

node* initOneNode(valueType value) {

node* tmp = (node*)malloc(sizeof(node));
memset(tmp, 0, sizeof(node));
tmp->value = value;

return tmp;
}

/* 初始化一个有环的链表,返回链表头结点指针 */
node* initOneCircleList(int size,int crossPosition) {
node *array[size];
node * prev = NULL;
for (int i = 0; i < size; i++) {
array[i] = initOneNode(i);
//cout<<"initOneCircleList value:"<<array[i]->value<<endl;
//cout<<"initOneCircleList address:"<<array[i]<<endl;

if (NULL != prev) {
prev->next = array[i];
}

prev = array[i];
//cout<<"prev address:"<<prev<<endl;

}

array[size-1]->next = array[crossPosition-1];
#if 0
node * tmp = array[0];
for (int i = 0; i < size*2; i++) {
cout<<"array["<<i<<"] address:"<<tmp<<endl;
tmp = tmp->next;
}
#endif
return array[0];
}

/* 初始化一个有环的链表,返回链表头结点指针 */
node* initOneNoCircleList(int size)
{
node* tmp = NULL;
node* prev = initOneNode(0);
node* head = prev;
for (int i = 1; i < size; i++) {
tmp = initOneNode(i);
prev->next = tmp;
prev = tmp;
}

#if 0
tmp = head;
for (int i = 0; i < size; i++) {
cout<<"tmp address:"<<tmp<<endl;
tmp = tmp->next;
}
#endif

return head;
}

/* 测试两条有环不相交链表 */
int test_case_2(node* h1, node* h2) {

h1 = initOneCircleList(5, 3);
h2 = initOneCircleList(5, 3);

return 0;
}

/* 测试两条无环相交链表 */
int test_case_3(node* h1, node* h2) {

h1 = initOneNoCircleList(5);
h2 = initOneNoCircleList(4);
node* tmp = h2;

while (NULL != tmp->next) {
tmp = tmp->next;
}

tmp->next = h1;

return 0;
}

/* 测试两条无环不相交链表 */
int test_case_4(node* h1, node* h2) {

h1 = initOneNoCircleList(5);
h2 = initOneNoCircleList(4);

return 0;
}
/* 测试两条有环相交链表 */
int test_case_1(node** h1,node** h2) {

*h1 = initOneCircleList(5, 3);
*h2 = initOneNoCircleList(3);

node* tmp_1 = *h1;
node* tmp_2 = *h2;

while (NULL != tmp_2->next) {
//       cout<<"tmp_1 address:"<<tmp_1<<endl;
//       cout<<"tmp_2 address:"<<tmp_2<<endl;

tmp_1 = tmp_1->next;
tmp_2 = tmp_2->next;
}

tmp_2->next = tmp_1;

#if 0
tmp_1 = h1;
tmp_2 = h2;
for(int i = 0; i < 5*2; i++) {
cout<<"tmp_1 address:"<<tmp_1<<endl;
cout<<"tmp_2 address:"<<tmp_2<<endl;
tmp_1 = tmp_1->next;
tmp_2 = tmp_2->next;
}
#endif

cout<<"h1:"<<*h1<<" h2:"<<*h2<<endl;
return 0;
}

int test_7() {
node *h1 = NULL;
node *h2 = NULL;

test_case_1(&h1, &h2);
//test_case_2(h1, h2);
//test_case_3(h1, h2);
//test_case_4(h1, h2);

cout<<"h1 address:"<<h1<<" h2 address:"<<h2<<endl;
DoubleListOfCrossing obj(h1,h2);

if ( obj.isCrossing())
cout<<"list h1 & h2 is crossing."<<endl;
else
cout<<"list h1 & h2 is not crossing"<<endl;

return 0;
}

int main(int argc, const char * argv[]) {

//test_1();
//test_2();
//test_3();
//test_4();
//maxHeap_test();
//test_5_1();
test_7();

return 0;
}


经过测试程序测试,以上代码能够跑通。下面是解决这个题目的完整版的代码。

先看头文件:

//
//  7th.h
//  100-alg-tests
//
//  Created by bobkentt on 15-4-11.
//  Copyright (c) 2015年 kedong. All rights reserved.
//

#ifndef ___00_alg_tests___th__
#define ___00_alg_tests___th__

#include <stdio.h>

#define valueType int

struct node {
valueType value;
node * next;
};

class DoubleListOfCrossing {

/* 判断一个链表是否有环 */
node* isCircle(node * head);

/// 判断两个链表是否相交
bool isCrossingOfNoCircle();
bool isCrossingOfCircle();

/* 找到一个带环链表的环结点. */
node* findCircleNode(node * head);
bool isExist(node* head,node * element);

node * m_h1;
node * m_h2;

public:
DoubleListOfCrossing(node * h1, node * h2) { m_h1 = h1;m_h2 = h2;};
~DoubleListOfCrossing() {};

bool isCrossing();

/* @type == 1 print m_h1;@type==2 print m_h2 */
void printList(bool type);

};

// 测试程序
int test_7();

#endif /* defined(___00_alg_tests___th__) */


再看一下源码实现和测试程序

//
// 7th.cpp
// 100-alg-tests
//
// Created by bobkentt on 15-4-11.
// Copyright (c) 2015年 kedong. All rights reserved.
//
#include <iostream>
#include <stdlib.h>

#include "7th.h"

using namespace std;

/*题目要求:
微软亚院之编程判断俩个链表是否相交
给出俩个单向链表的头指针,比如h1,h2,判断这俩个链表是否相交。
为了简化问题,我们假设俩个链表均不带环。

问题扩展:
1.如果链表可能有环列?
2.如果需要求出俩个链表相交的第一个节点列?
*/

/// 判断一个链表是否有环
/*
假设单链表的总长度为L,头结点到环入口的距离为a,
环入口到快慢指针相遇的结点距离为x,环的长度为r,
慢指针总共走了s步,则快指针走了2s步。另外,
快指针要追上慢指针的话快指针至少要在环里面转了
一圈多(假设转了n圈加x的距离),得到以下关系:
s = a + x;
2s = a + nr + x;
=>a + x = nr;
=>a = nr - x;
由上式可知:若在头结点和相遇结点分别设一指针,
同步(单步)前进,则最后一定相遇在环入口结点.
*/
/*判断链表是否有环,如果有环则返回环的首结点指针,否则返回NULL值*/
node* DoubleListOfCrossing::isCircle(node * head) { if (NULL == head) { return NULL; } node* p1 = head; node* p2 = head; do { if (NULL != p1->next && NULL != p2->next && NULL != p2->next->next) { p1 = p1->next; p2 = p2->next->next; } else { return NULL; } }while (p1 != p2); p2 = head; while (p1 != p2) { p1 = p1->next; p2 = p2->next; } return p1; }

node* DoubleListOfCrossing::findCircleNode(node * head) {
return isCircle(head);
}

/* 不带环的情况下判断两个链表是否相交 */ bool DoubleListOfCrossing::isCrossingOfNoCircle() { if (NULL == m_h1 || NULL == m_h2) { return NULL; } /* 把链表2接到链表1上. */ node * tail1 = m_h1; while (NULL != tail1->next) { tail1 = tail1->next; } tail1->next = m_h2; return (NULL == isCircle(m_h1)) ? false : true; }

/* 判断一个结点是否在链表上. */
bool DoubleListOfCrossing::isExist(node* head,node * element) {
if (NULL == head) {
return false;
}

node * tmp = head;
while (NULL != tmp->next) {
if (tmp == element) {
return true;
} else {
tmp = tmp->next;
}
}

return false;
}

/* 两个链表都带环的情况下,判断第一个链表的环结点是否在第二个链表上 */
bool DoubleListOfCrossing::isCrossingOfCircle() {
node * cross = findCircleNode(m_h1);
return isExist(m_h2, cross);
}

/*
* @param <type> <true> 打印链表1;
* <false>打印链表2.
*/
void DoubleListOfCrossing::printList(bool type) {

node * tail = (true == type) ? m_h1 : m_h2;

if (NULL == tail) {
return;
}

while (NULL != tail->next) {
cout<<tail->value<<" ";
tail = tail->next;
}

cout<<endl;

return;
}

bool DoubleListOfCrossing::isCrossing() { #define IS_CIRCLE(a) NULL != isCircle((a)) #define NOT_CIRCLE(a) NULL == isCircle((a)) if (NULL == m_h1 && NULL == m_h2) { cout<<"NULL == m_h1 && NULL == m_h2"<<endl; return false; } bool bothNotCircle = (NOT_CIRCLE(m_h1) && NOT_CIRCLE(m_h2)) ? true : false; bool bothCircle = (IS_CIRCLE(m_h1) && IS_CIRCLE(m_h2)) ? true : false; /* 判断两个链表是否都有环,若都没有环 */ if (bothNotCircle) { return isCrossingOfNoCircle(); } /* 若都有环 */ if (bothCircle) { return isCrossingOfCircle(); } /* 若一个有环,一个没有环则两个链表不可能相交 */ return false; }
node* initOneNode(valueType value) {

node* tmp = (node*)malloc(sizeof(node));
memset(tmp, 0, sizeof(node));
tmp->value = value;

return tmp;
}

/* 初始化一个有环的链表,返回链表头结点指针 */
node* initOneCircleList(int size,int crossPosition) {
node *array[size];
node * prev = NULL;
for (int i = 0; i < size; i++) {
array[i] = initOneNode(i);
//cout<<"initOneCircleList value:"<<array[i]->value<<endl;
//cout<<"initOneCircleList address:"<<array[i]<<endl;

if (NULL != prev) {
prev->next = array[i];
}

prev = array[i];
//cout<<"prev address:"<<prev<<endl;

}

array[size-1]->next = array[crossPosition-1];
#if 0
node * tmp = array[0];
for (int i = 0; i < size*2; i++) {
cout<<"array["<<i<<"] address:"<<tmp<<endl;
tmp = tmp->next;
}
#endif
return array[0];
}

/* 初始化一个有环的链表,返回链表头结点指针 */
node* initOneNoCircleList(int size)
{
node* tmp = NULL;
node* prev = initOneNode(0);
node* head = prev;
for (int i = 1; i < size; i++) {
tmp = initOneNode(i);
prev->next = tmp;
prev = tmp;
}

#if 0
tmp = head;
for (int i = 0; i < size; i++) {
cout<<"tmp address:"<<tmp<<endl;
tmp = tmp->next;
}
#endif

return head;
}

/* 测试两条有环不相交链表 */
int test_case_2(node* h1, node* h2) {

h1 = initOneCircleList(5, 3);
h2 = initOneCircleList(5, 3);

return 0;
}

/* 测试两条无环相交链表 */
int test_case_3(node* h1, node* h2) {

h1 = initOneNoCircleList(5);
h2 = initOneNoCircleList(4);
node* tmp = h2;

while (NULL != tmp->next) {
tmp = tmp->next;
}

tmp->next = h1;

return 0;
}

/* 测试两条无环不相交链表 */
int test_case_4(node* h1, node* h2) {

h1 = initOneNoCircleList(5);
h2 = initOneNoCircleList(4);

return 0;
}
/* 测试两条有环相交链表 */
int test_case_1(node** h1,node** h2) {

*h1 = initOneCircleList(5, 3);
*h2 = initOneNoCircleList(3);

node* tmp_1 = *h1;
node* tmp_2 = *h2;

while (NULL != tmp_2->next) {
// cout<<"tmp_1 address:"<<tmp_1<<endl;
// cout<<"tmp_2 address:"<<tmp_2<<endl;

tmp_1 = tmp_1->next;
tmp_2 = tmp_2->next;
}

tmp_2->next = tmp_1;

#if 0
tmp_1 = h1;
tmp_2 = h2;
for(int i = 0; i < 5*2; i++) {
cout<<"tmp_1 address:"<<tmp_1<<endl;
cout<<"tmp_2 address:"<<tmp_2<<endl;
tmp_1 = tmp_1->next;
tmp_2 = tmp_2->next;
}
#endif

cout<<"h1:"<<*h1<<" h2:"<<*h2<<endl;
return 0;
}

int test_7() {
node *h1 = NULL;
node *h2 = NULL;

test_case_1(&h1, &h2);
//test_case_2(h1, h2);
//test_case_3(h1, h2);
//test_case_4(h1, h2);

cout<<"h1 address:"<<h1<<" h2 address:"<<h2<<endl;
DoubleListOfCrossing obj(h1,h2);

if ( obj.isCrossing())
cout<<"list h1 & h2 is crossing."<<endl;
else
cout<<"list h1 & h2 is not crossing"<<endl;

return 0;
}


最后看一下主函数

//
//  main.cpp
//  100-alg-tests
//
//  Created by bobkentt on 15-3-24.
//  Copyright (c) 2015年 kedong. All rights reserved.
//

#include <iostream>

//#include "2th.h"
//#include "3th.h"
//#include "4th.h"
//#include "maxHeap.h"
//#include "5th.h"
#include "7th.h"

int main(int argc, const char * argv[]) {

//test_1();
//test_2();
//test_3();
//test_4();
//maxHeap_test();
//test_5_1();
test_7();

return 0;
}


Okay,第7题的完整解题过程就到这里了,希望大家多指教。

路漫漫其修远兮,吾将上下而…
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息