算法设计题整理

http://www.cnblogs.com/mfryf/archive/2012/07/31/2616697.html

1.一个整数数列,元素取值可能是0~65535中的任意一个数,相同数值不会重复出现。0是例外,可以反复出现。

请设计一个算法,当你从该数列中随意选取5个数值,判断这5个数值是否连续相邻。

注意:

- 5个数值允许是乱序的。比如: 8 7 5 0 6

- 0可以通配任意数值。比如:8 7 5 0 6 中的0可以通配成9或者4

- 0可以多次出现。

- 复杂度如果是O(n2)则不得分。

思路:非0最大-非0最小+1 <=5 ==> 非0最大-非0最小 <=4


2.设计一个算法,找出二叉树上任意两个结点的最近共同父结点。

复杂度如果是O(n2)则不得分。

思路:如果每个节点包含父亲指针,把两个节点到根的路径都记录下来,两条路径的最后面的元素肯定相同,从两条路径的最后一个元素向前比较,直到第一次出现分叉为止,就可以找到最近节点。复杂度为O(n),路径最长可能是n。如果不包含父亲节点,那就先前序遍历二叉树,遍历的时候可以像哈夫曼树那样左右01编号,记录给定两节点的到达路径,最后比较两个0,1序列的前面位数,直到出现不相等为止,就找到最近父节点,复杂度也是O(n)


3.一棵排序二叉树,令 f=(最大值+最小值)/2,设计一个算法,找出距离f值最近、大于f值的结点。

复杂度如果是O(n2)则不得分。

思路:找出最大值,最小值,复杂度都是O(h),然后搜索f,可以找到f应该插入的位置,复杂度也是O(h),再找f的后继,复杂度也是O(h),h最大可能是n,所以总体最坏情况复杂度就是O(n)


4.一个整数数列,元素取值可能是1~N(N是一个较大的正整数)中的任意一个数,相同数值不会重复出现。设计一个算法,找出数列中符合条件的数对的个数,满足数对中两数的和等于N+1。

复杂度最好是O(n),如果是O(n2)则不得分。

思路:先排序,复杂度O(nlgn),然后用两个指示器(front和back)分别指向第一个和最后一个元素,

如果A[front]+A[back]>N+1,则back–;

如果A[front]+A[back]=N+1,则计数器加1,back–,同时front++;

如果A[front]+A[back]重复上述步骤,O(n)时间找到所有数对,总体复杂度为O(nlgn)


5.输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。

要求不能创建任何新的结点,只调整指针的指向。

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

};

思路一:当我们到达某一结点准备调整以该结点为根结点的子树时,先调整其左子树将左子树转换成一个排好序的左子链表,再调整其右子树转换右子链表。最近链接左子链表的最右结点(左子树的最大结点)、当前结点和右子链表的最左结点(右子树的最小结点)。从树的根结点开始递归调整所有结点。

思路一对应的代码:

BSTreeNode* ConvertNode(BSTreeNode* pNode, bool asRight){

if(!pNode)

return NULL;

BSTreeNode *pLeft = NULL;

BSTreeNode *pRight = NULL;

// Convert the left sub-tree

if(pNode->m_pLeft)

pLeft = ConvertNode(pNode->m_pLeft, false);

// Connect the greatest node in the left sub-tree to the current node

if(pLeft)

{

pLeft->m_pRight = pNode;

pNode->m_pLeft = pLeft;

}

// Convert the right sub-tree

if(pNode->m_pRight)

pRight = ConvertNode(pNode->m_pRight, true);

// Connect the least node in the right sub-tree to the current node

if(pRight){

pNode->m_pRight = pRight;

pRight->m_pLeft = pNode;

}

BSTreeNode *pTemp = pNode;

// If the current node is the right child of its parent,

// return the least node in the tree whose root is the current node

if(asRight){

while(pTemp->m_pLeft)

pTemp = pTemp->m_pLeft;

}

// If the current node is the left child of its parent,

// return the greatest node in the tree whose root is the current node

else{

while(pTemp->m_pRight)

pTemp = pTemp->m_pRight;

}

return pTemp;

}

BSTreeNode* Convert(BSTreeNode* pHeadOfTree)

{

// As we want to return the head of the sorted double-linked list,

// we set the second parameter to be true

return ConvertNode(pHeadOfTree, true);

}

思路二:

我们可以中序遍历整棵树。按照这个方式遍历树,比较小的结点先访问。如果我们每访问一个结点,假设之前访问过的结点已经调整成一个排序双向链表,我们再把调整当前结点的指针将其链接到链表的末尾。当所有结点都访问过之后,整棵树也就转换成一个排序双向链表了。

void ConvertNode(BSTreeNode* pNode, BSTreeNode*& pLastNodeInList){

if(pNode == NULL)

return;

BSTreeNode *pCurrent = pNode;

// Convert the left sub-tree

if (pCurrent->m_pLeft != NULL)

ConvertNode(pCurrent->m_pLeft, pLastNodeInList);

// Put the current node into the double-linked list

pCurrent->m_pLeft = pLastNodeInList;

if(pLastNodeInList != NULL)

pLastNodeInList->m_pRight = pCurrent;

pLastNodeInList = pCurrent;

// Convert the right sub-tree

if (pCurrent->m_pRight != NULL)

ConvertNode(pCurrent->m_pRight, pLastNodeInList);

}

///////////////////////////////////////////////////////////////////////

// Covert a binary search tree into a sorted double-linked list

// Input: pHeadOfTree - the head of tree

// Output: the head of sorted double-linked list

///////////////////////////////////////////////////////////////////////

BSTreeNode* Convert_Solution1(BSTreeNode* pHeadOfTree)

{

BSTreeNode *pLastNodeInList = NULL;

ConvertNode(pHeadOfTree, pLastNodeInList);

// Get the head of the double-linked list

BSTreeNode *pHeadOfList = pLastNodeInList;

while(pHeadOfList && pHeadOfList->m_pLeft)

pHeadOfList = pHeadOfList->m_pLeft;

return pHeadOfList;

}


6.设计包含min函数的栈。定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素。要求函数min、push以及pop的时间复杂度都是O(1)。

思路:这是google的一道面试题。

看到这道题目时,第一反应就是每次push一个新元素时,将栈里所有逆序元素排序。这样栈顶元素将是最小元素。但由于不能保证最后push进栈的元素最先出栈,这种思路设计的数据结构已经不是一个栈了。

在栈里添加一个成员变量存放最小元素(或最小元素的位置)。每次push一个新元素进栈的时候,如果该元素比当前的最小元素还要小,则更新最小元素。

乍一看这样思路挺好的。但仔细一想,该思路存在一个重要的问题:如果当前最小元素被pop出去,如何才能得到下一个最小元素?

因此仅仅只添加一个成员变量存放最小元素(或最小元素的位置)是不够的。我们需要一个辅助栈。每次push一个新元素的时候,同时将最小元素push到辅助栈中;

//或最小元素的位置。考虑到栈元素的类型可能是复杂的数据结构,用最小元素的位置将能减少空间消耗。

每次pop一个元素出栈的时候,同时pop辅助栈。

参考代码:

#include 

#include 

template  class CStackWithMin

{

public:

CStackWithMin(void) {}

virtual ~CStackWithMin(void) {}

T& top(void);

const T& top(void) const;

void push(const T& value);

void pop(void);

const T& min(void) const;

private:

T>m_data;// theelements of stack

size_t>m_minIndex;// the indicesof minimum elements

};

// get the last element of mutable stack

template  T& CStackWithMin::top()

{

return m_data.back();

}

// get the last element of non-mutable stack

template  const T& CStackWithMin::top() const

{

return m_data.back();

}

// insert an elment at the end of stack

template  void CStackWithMin::push(const T& value)

{

// append the data into the end of m_data

m_data.push_back(value);

// set the index of minimum elment in m_data at the end of m_minIndex

if(m_minIndex.size() == 0)

m_minIndex.push_back(0);

else

{

if(value < m_data[m_minIndex.back()])

m_minIndex.push_back(m_data.size() - 1);

else

m_minIndex.push_back(m_minIndex.back());

}

}

// erease the element at the end of stack

template  void CStackWithMin::pop()

{

// pop m_data

m_data.pop_back();

// pop m_minIndex

m_minIndex.pop_back();

}

// get the minimum element of stack

template  const T& CStackWithMin::min() const

{

assert(m_data.size() > 0);

assert(m_minIndex.size() > 0);

return m_data[m_minIndex.back()];

}


6.求子数组的最大和(更高效的思路见编程珠玑83页)

题目:输入一个整形数组,数组里有正数也有负数。

数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。

求所有子数组的和的最大值。要求时间复杂度为O(n)。

例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,

因此输出为该子数组的和18。

如果不考虑时间复杂度,我们可以枚举出所有子数组并求出他们的和。

不过非常遗憾的是,由于长度为n的数组有O(n2)个子数组;

而且求一个长度为n的数组的和的时间复杂度为O(n)。因此这种思路的时间是O(n3)。

当我们加上一个正数时,和会增加;当我们加上一个负数时,和会减少。

如果当前得到的和是个负数,那么这个和在接下来的累加中应该抛弃并重新清零,

不然的话这个负数将会减少接下来的和。基于这样的思路,我们可以写出如下代码。

参考代码:

// Find the greatest sum of all sub-arrays

// Return value: if the input is valid, return true, otherwise return false

/////////////////////////////////////////////////////////////////////////////

bool FindGreatestSumOfSubArray(

int *pData,           // an array

unsigned int nLength, // the length of array

int &nGreatestSum     // the greatest sum of all sub-arrays

)

{

// if the input is invalid, return false

if((pData == NULL) || (nLength == 0))

return false;

int nCurSum = nGreatestSum = 0;

for(unsigned int i = 0; i < nLength; ++i)

{

nCurSum += pData[i];

// if the current sum is negative, discard it

if(nCurSum < 0)

nCurSum = 0;

// if a greater sum is found, update the greatest sum

if(nCurSum > nGreatestSum)

nGreatestSum = nCurSum;

}

// if all data are negative, find the greatest element in the array

if(nGreatestSum == 0)

{

nGreatestSum = pData[0];

for(unsigned int i = 1; i < nLength; ++i)

{

if(pData[i] > nGreatestSum)

nGreatestSum = pData[i];

}

}

return true;

}


7.题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。句子中单词以空格符隔开。

为简单起见,标点符号和普通字母一样处理。

例如输入“I am a student.”,则输出“student. a am I”。

思路1:先将整个英文句子翻转,这样一来每个单词也都翻转了,在将每个单词翻转回去即可!

思路2:参照autoreleasepool添加哨兵指针的方式,第一次遍历给每个单词开头安插一个哨兵(index),然后将哨兵入栈。遍历完成后从栈顶开始遍历每个哨兵并输出哨兵索引对应的单词。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容

  • 课程介绍 先修课:概率统计,程序设计实习,集合论与图论 后续课:算法分析与设计,编译原理,操作系统,数据库概论,人...
    ShellyWhen阅读 2,162评论 0 3
  • 1.把二元查找树转变成排序的双向链表 题目: 输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。 要求不...
    曲终人散Li阅读 3,242评论 0 19
  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,663评论 0 33
  • 第一章 绪论 什么是数据结构? 数据结构的定义:数据结构是相互之间存在一种或多种特定关系的数据元素的集合。 第二章...
    SeanCheney阅读 5,660评论 0 19
  • 老孙是国营理发店的理发师傅,理得一手的好平头,这些年生意日渐稀少,他给男的理发还行,样式古老,跟不上什么板寸了,毛...
    一言一语一行阅读 463评论 0 2