1.把二元查找树转变成排序的双向链表
题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。
要求不能创建任何新的结点,只调整指针的指向。
10
/ \
6 14
/ \ / \
4 8 12 16
转换成双向链表
4=6=8=10=12=14=16。
首先我们定义的二元查找树节点的数据结构如下:
struct BSTreeNode
{
int m_nValue; // value of node
BSTreeNode *m_pLeft; // left child of node
BSTreeNode *m_pRight; // right child of node
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
// 1:构造二叉查找树;
// 2:中序遍历二叉查找树,因此结点按从小到大顺序访问,假设之前访问过的结点已经调整为一个双向链表,那么
// 只需要将当前结点连接至双向链表的最后一个结点即可,访问完后,双向链表也就调整完了
#include <iostream>
using
namespace
std;
struct
BSTreeNode
{
int
m_nValue;
// value of node
BSTreeNode *m_pLeft;
// left child of node
BSTreeNode *m_pRight;
// right child of node
};
void
addBSTreeNode(BSTreeNode *&pCurrent,
int
value);
void
inOrderBSTree(BSTreeNode* pBSTree);
void
convertToDoubleList(BSTreeNode* pCurrent);
BSTreeNode *pHead=NULL;
//指向循环队列头结点
BSTreeNode *pIndex=NULL;
//指向前一个结点
int
main()
{
BSTreeNode *pRoot=NULL;
addBSTreeNode(pRoot,10);
addBSTreeNode(pRoot,6);
addBSTreeNode(pRoot,14);
addBSTreeNode(pRoot,4);
addBSTreeNode(pRoot,8);
addBSTreeNode(pRoot,12);
addBSTreeNode(pRoot,16);
inOrderBSTree(pRoot);
return
0;
}
/************************************************************************/
/* 建立二叉排序树 */
void
addBSTreeNode(BSTreeNode *&pCurrent,
int
value)
//在这个函数中会要改变指针值,一定要记得使用引用传递
{
if
(pCurrent==NULL)
{
BSTreeNode* pBSTree=
new
BSTreeNode();
pBSTree->m_nValue=value;
pBSTree->m_pLeft=NULL;
pBSTree->m_pRight=NULL;
pCurrent=pBSTree;
}
else
if
(pCurrent->m_nValue<value)
{
addBSTreeNode(pCurrent->m_pRight,value);
}
else
if
(pCurrent->m_nValue>value)
{
addBSTreeNode(pCurrent->m_pLeft,value);
}
else
{
cout<<
"node repeated"
<<endl;
}
}
/************************************************************************/
/************************************************************************/
/* 中序遍历二叉树,同时调整结点指针 */
void
inOrderBSTree(BSTreeNode* pBSTree)
{
if
(NULL==pBSTree)
{
return
;
}
if
(NULL!=pBSTree->m_pLeft)
{
inOrderBSTree(pBSTree->m_pLeft);
}
// if (NULL!=pBSTree)
// {
// cout<<pBSTree->m_nValue;
// }
convertToDoubleList(pBSTree);
if
(NULL!=pBSTree->m_pRight)
{
inOrderBSTree(pBSTree->m_pRight);
}
}
/************************************************************************/
/************************************************************************/
/* 调整结点指针 */
void
convertToDoubleList(BSTreeNode* pCurrent)
{
pCurrent->m_pLeft=pIndex;
//使当前结点的左指针指向双向链表中最后一个结点
if
(NULL==pIndex)
//若最后一个元素不存在,此时双向链表尚未建立,因此将当前结点设为双向链表头结点
{
pHead=pCurrent;
}
else
//使双向链表中最后一个结点的右指针指向当前结点
{
pIndex->m_pRight=pCurrent;
}
pIndex=pCurrent;
//将当前结点设为双向链表中最后一个结点
cout<<pCurrent->m_nValue<<
" "
;
}
/************************************************************************/
2:
计包含min函数的栈要求定义栈的数据结构,要求添加一个min 函数,能够得到栈的最小元素。 要求函数min、push 以及pop 的时间复杂度都是O(1)。
解法
使用一个辅助栈来保存最小元素,这个解法简单不失优雅。设该辅助栈名字为minimum stack,其栈顶元素为当前栈中的最小元素。这意味着
3:题目:输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。要求时间复杂度为O(n)。 一种从前往后遍历的方法如下:
4 面试题目: 输入一个整数和一棵二元树。从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。打印出和与输入整数相等的所有路径。 则打印出两条路径:10, 12和10, 5, 7。
思路:对于如图所示的二叉树,其实就是一个递归的过程,上例可以有三条路径10 5 4 ,10 5 7,10 12,递归调用者三次路径即可,如果满足就打印出来,不可以就再重复递归
代码如下:
需要注意的是出栈与入栈的处理,友情是这一步
sum -= root->m_nValue;
5:题目是:输入n 个整数,输出其中最小的k 个。例如输入1,2,3,4,5,6,7 和8 这8 个数字,则最小的4 个数字为1,2,3 和4。 问题分析:先用冒泡排序法将数列排序,然后输出其中最小的k个数,注意输入输出格式问题(空格问题) 代码:
方法:使用p、q两个指针,p每次向前走一步,q每次向前走两步,若在某个时候p == q,则存在环。
#include <stdio.h> 2 #include <stdlib.h> 3 4 #define LEN 8 5 typedef struct node* node_t; 6 7 struct node{ 8 char val; 9 struct node *next; 10 }; 11 12 //method 1 13 int has_loop(struct node *head); 14 //method 2 15 int has_loop2(node_t head); 16 17 int main() 18 { 19 node_t* arr = (node_t*)malloc(sizeof(struct node)*LEN); 20 arr[0] = (node_t)malloc(sizeof(struct node)); 21 int i; 22 for(i = 1; i < LEN; i++) 23 { 24 arr[i] = (node_t)malloc(sizeof(struct node)); 25 arr[i - 1]->next = arr[i]; 26 } 27 arr[LEN - 1]->next = NULL; 28 29 //you can add a loop here to test 30 //arr[6]->next = arr[0]; 31 if (has_loop(arr[0])) 32 printf("method1: has loop.\n"); 33 else 34 printf("method1: has no loop.\n"); 35 36 if (has_loop2(arr[0])) 37 printf("method2: has loop.\n"); 38 else 39 printf("method2: has no loop.\n"); 40 41 return 0; 42 } 43 44 //if two pointer are equal, but they don't have the same steps, then has a loop 45 int has_loop(node_t head) 46 { 47 node_t cur1 = head; 48 int pos1 = 0; 49 while(cur1){ 50 node_t cur2 = head; 51 int pos2 = 0; 52 pos1 ++; 53 while(cur2){ 54 pos2 ++; 55 if(cur2 == cur1){ 56 if(pos1 == pos2) 57 break; 58 else 59 return 1; 60 } 61 cur2 = cur2->next; 62 } 63 cur1 = cur1->next; 64 } 65 return 0; 66 } 67 68 //using step1 and step2 here 69 //if exists a loop, then the pointer which use step2 will catch up with the pointer which uses step1 70 int has_loop2(node_t head) 71 { 72 node_t p = head; 73 node_t q = head; 74 while (p != NULL && q != NULL) 75 { 76 /* 77 p = p->next; 78 if (q->next != NULL) 79 q = q->next->next; 80 if (p == q) 81 return 1; 82 */ 83 //correct it on 17/11/2012 84 p = p->next; 85 q = q->next; 86 if (q != NULL) 87 q = q->next; 88 if (p != NULL && p == q) 89 return 1; 90 } 91 return 0; 92 }
第7题
微软亚院之编程判断俩个链表是否相交
一个比较经典的问题,判断两个链表是否相交,如果相交找出他们的交点。 思路: 1、碰到这个问题,第一印象是采用hash来判断,将两个链表的节点进行hash,然后判断出节点,这种想法当然是可以的。 2、当然采用暴力的方法也是可以的,遍历两个链表,在遍历的过程中进行比较,看节点是否相同。 3、第三种思路是比较奇特的,在编程之美上看到的。先遍历第一个链表到他的尾部,然后将尾部的next指针指向第二个链表(尾部指针的next本来指向的是null)。这样两个链表就合成了一个链表,判断原来的两个链表是否相交也就转变成了判断新的链表是否有环的问题了:即判断单链表是否有环? 这样进行转换后就可以从链表头部进行判断了,其实并不用。通过简单的了解我们就很容易知道,如果新链表是有环的,那么原来第二个链表的头部一定在环上。因此我们就可以从第二个链表的头部进行遍历的,从而减少了时间复杂度(减少的时间复杂度是第一个链表的长度)。 下图是一个简单的演示: 这种方法可以判断两个链表是否相交,但不太容易找出他们的交点。 4、仔细研究两个链表,如果他们相交的话,那么他们最后的一个节点一定是相同的,否则是不相交的。因此判断两个链表是否相交就很简单了,分别遍历到两个链表的尾部,然后判断他们是否相同,如果相同,则相交;否则不相交。示意图如下: 判断出两个链表相交后就是判断他们的交点了。假设第一个链表长度为len1,第二个问len2,然后找出长度较长的,让长度较长的链表指针向后移动|len1 - len2| (len1-len2的绝对值),然后在开始遍历两个链表,判断节点是否相同即可。 下面给出一个简单的实现: typedef struct node_t {
}node;
node* find_node(node *head1, node *head2) {
} 通过上面的操作就可以找到两个链表的交点了。
8
逆转单向链表
点击(此处)折叠或打开
9: 整数序列是不是二元查找树的后序遍历结果题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果。如果是返回true,否则返回false。 例如输入5、7、6、9、11、10、8,由于这一整数序列是如下树的后序遍历结果: 8 因此返回true。 如果输入7、4、6、5,没有哪棵树的后序遍历的结果是这个序列,因此返回false。 树的题目一般都是递归的思路,因为是因为是排序树,而且是后序,所以思路是序列最后一个必是根节点,从前往后遍历,比根节点小的都是其左子树,并且位于序列的左半部分,比其大的为其右子树,应该位于其右半部分。左右子树按上述思路分别进行判断。 代码如下:
二元查找树的定义为: (1)若左子树不空,则左子树上所有节点的值均小于其根节点的值。 (2)若右子树不空,则右子树上所有节点的值均小于其跟节点的值 (3)其左右子树也均为二叉查找树。 那么先给定一个数字序列5、7、6、9、11、10、8,判断这个序列是否是二元查找树的后根遍历。可以回想一下后序遍历的性质,先访问左子树,然后访问右子树,最后访问根节点。结合以上这些性质,就可以理解到,应该从给定序列的最后一个元素开始,最后一个元素设定为根节点,循环遍历一次数组,找左右子树的分界点,上面这个例子就是6,9之间。这样可以判断,5、6、7,这三个数为以8为根节点的左子树上的节点,9、11、10为以8为根节点的右子树的根节点。如果在对以上分出来的两个数组做同样的操作,那么就可以解决这个问题。综上所述,可以应用递归方法,递归的关键是找到结束的条件。下面给出代码,并进行两组测试。
|