收藏 分享(赏)

算法大全-面试题-数据结构.docx

上传人:dzzj200808 文档编号:2704920 上传时间:2018-09-25 格式:DOCX 页数:54 大小:47.25KB
下载 相关 举报
算法大全-面试题-数据结构.docx_第1页
第1页 / 共54页
算法大全-面试题-数据结构.docx_第2页
第2页 / 共54页
算法大全-面试题-数据结构.docx_第3页
第3页 / 共54页
算法大全-面试题-数据结构.docx_第4页
第4页 / 共54页
算法大全-面试题-数据结构.docx_第5页
第5页 / 共54页
点击查看更多>>
资源描述

1、一、单链表目录1.单链表反转2.找出单链表的倒数第 4 个元素3.找出单链表的中间元素4.删除无头单链表的一个节点5.两个不交叉的有序链表的合并6.有个二级单链表,其中每个元素都含有一个指向一个单链表的指针。写程序把这个二级链表称一级单链表。7.单链表交换任意两个元素(不包括表头)8.判断单链表是否有环?如何找到环的“起始”点?如何知道环的长度?9.判断两个单链表是否相交10.两个单链表相交,计算相交点11.用链表模拟大整数加法运算12.单链表排序13.删除单链表中重复的元素首先写一个单链表的 C#实现,这是我们的基石:public class Linkpublic Link Next;pub

2、lic string Data;public Link(Link next, string data)this.Next = next;this.Data = data;其中,我们需要人为地在单链表前面加一个空节点,称其为 head。例如,一个单链表是 1-2-5,如图所示:对一个单链表的遍历如下所示:static void Main(string args)Link head = GenerateLink();Link curr = head;while (curr != null)Console.WriteLine(curr.Data);curr = curr.Next;1.单链表反转这道

3、题目有两种算法,既然是要反转,那么肯定是要破坏原有的数据结构的:算法 1:我们需要额外的两个变量来存储当前节点 curr 的下一个节点 next、再下一个节点 nextnext:public static Link ReverseLink1(Link head)Link curr = head.Next;Link next = null;Link nextnext = null;/if no elements or only one element existsif (curr = null | curr.Next = null)return head;/if more than one el

4、ementwhile (curr.Next != null)next = curr.Next; /1nextnext = next.Next; /2next.Next = head.Next; /3head.Next = next; /4curr.Next = nextnext; /5return head;算法的核心是 while 循环中的 5 句话我们发现,curr 始终指向第 1 个元素。此外,出于编程的严谨性,还要考虑 2 种极特殊的情况:没有元素的单链表,以及只有一个元素的单链表,都是不需要反转的。算法 2:自然是递归如果题目简化为逆序输出这个单链表,那么递归是很简单的,在递归函数之

5、后输出当前元素,这样能确保输出第 N 个元素语句永远在第 N+1 个递归函数之后执行,也就是说第 N 个元素永远在第 N+1 个元素之后输出,最终我们先输出最后一个元素,然后是倒数第 2 个、倒数第 3 个,直到输出第 1 个:public static void ReverseLink2(Link head)if (head.Next != null)ReverseLink2(head.Next);Console.WriteLine(head.Next.Data);但是,现实应用中往往不是要求我们逆序输出(不损坏原有的单链表) ,而是把这个单链表逆序(破坏型) 。这就要求我们在递归的时候,还

6、要处理递归后的逻辑。首先,要把判断单链表有 0 或 1 个元素这部分逻辑独立出来,而不需要在递归中每次都比较一次:public static Link ReverseLink3(Link head)/if no elements or only one element existsif (head.Next = null | head.Next.Next = null)return head;head.Next = ReverseLink(head.Next);return head;我们观测到:head.Next = ReverseLink(head.Next);这句话的意思是为 Rever

7、seLink 方法生成的逆序链表添加一个空表头。接下来就是递归的核心算法 ReverseLink 了:static Link ReverseLink(Link head)if (head.Next = null)return head;Link rHead = ReverseLink(head.Next);head.Next.Next = head;head.Next = null;return rHead;算法的关键就在于递归后的两条语句:head.Next.Next = head; /1head.Next = null; /2啥意思呢?画个图表示就是:这样,就得到了一个逆序的单链表,我们只

8、用到了 1 个额外的变量 rHead。2.找出单链表的倒数第 4 个元素这道题目有两种算法,但无论哪种算法,都要考虑单链表少于 4 个元素的情况:第 1 种算法,建立两个指针,第一个先走 4 步,然后第 2 个指针也开始走,两个指针步伐(前进速度)一致。static Link GetLast4thOne(Link head)Link first = head;Link second = head;for (int i = 0; i head1 head2 head3 head4我们的算法思想是: 进行两次遍历,在外层用 curr1 遍历二级单链表 head,在内层用 curr2 遍历每个单链表

9、:public static Link GenerateNewLink(CascadeLink head)CascadeLink curr1 = head.NextHead;Link newHead = curr1.Next;Link curr2 = newHead;while (curr1 != null)curr2.Next = curr1.Next.Next;while (curr2.Next != null)curr2 = curr2.Next;curr1 = curr1.NextHead;return newHead;其中,curr2.Next = curr1.Next.Next;

10、这句话是关键,它负责把上一个单链表的表尾和下一个单链表的非空表头连接起来。7.单链表交换任意两个元素(不包括表头)先一次遍历找到这两个元素 curr1 和 curr2,同时存储这两个元素的前驱元素pre1 和 pre2。然后大换血public static Link SwitchPoints(Link head, Link p, Link q)if (p = head | q = head)throw new Exception(“No exchange with head“);if (p = q)return head;/find p and q in the linkLink curr =

11、 head;Link curr1 = p;Link curr2 = q;Link pre1 = null;Link pre2 = null;int count = 0;while (curr != null)if (curr.Next = p)pre1 = curr;count+;if (count = 2)break;else if (curr.Next = q)pre2 = curr;count+;if (count = 2)break;curr = curr.Next;curr = curr1.Next;pre1.Next = curr2;curr1.Next = curr2.Next;

12、pre2.Next = curr1;curr2.Next = curr;return head;注意特例,如果相同元素,就没有必要交换;如果有一个是表头,就不交换。8.判断单链表是否有环?如何找到环的“起始”点?如何知道环的长度?算法思想:先分析是否有环。为此我们建立两个指针,从 header 一起向前跑,一个步长为1,一个步长为 2,用 while(直到步长 2 的跑到结尾)检查两个指针是否相等,直到找到为止。static bool JudgeCircleExists(Link head)Link first = head; /1 step each timeLink second = he

13、ad; /2 steps each timewhile (second.Next != null first = first.Next;if (second = first)return true;return false;那又如何知道环的长度呢?根据上面的算法,在返回 true 的地方,也就是 2 个指针相遇处,这个位置的节点 P 肯定位于环上。我们从这个节点开始先前走,转了一圈肯定能回来:static int GetCircleLength(Link point)int length = 1;Link curr = point;while (curr.Next != point)lengt

14、h+;curr = curr.Next;return length;继续我们的讨论,如何找到环的“起始”点呢?延续上面的思路,我们仍然在返回 true 的地方 P,计算一下从有环单链表的表头 head 到 P 点的距离static int GetLengthFromHeadToPoint(Link head, Link point)int length = 1;Link curr = head;while (curr != point)length+;curr = curr.Next;return length;如果我们把环从 P 点“切开 ”(当然并不是真的切,那就破坏原来的数据结构了),那

15、么问题就转化为计算两个相交“单链表”的交点(第 10 题):一个单链表是从 P 点出发,到达 P(一个回圈) ,距离 M;另一个单链表从有环单链表的表头 head 出发,到达 P,距离 N。我们可以参考第 10 题的 GetIntersect 方法并稍作修改。private static Link FindIntersect(Link head)Link p = null;/get the point in the circlebool result = JudgeCircleExists(head, ref p);if (!result) return null;Link curr1 = h

16、ead.Next;Link curr2 = p.Next;/length from head to pint M = 1;while (curr1 != p)M+;curr1 = curr1.Next;/circle lengthint N = 1;while (curr2 != p)N+;curr2 = curr2.Next;/recover curr1 curr2 = p.Next;/make 2 links have the same distance to the intersectif (M N)for (int i = 0; i N)for (int i = 0; i 99NULL

17、 + 1NULL =1000NULL肯定是使用递归啦,不然没办法解决进位+1 问题,因为这时候要让前面的节点加 1,而我们的单链表是永远指向前的。此外对于 999+1=1000,新得到的值的位数(4 位)比原来的两个值(1 个 1 位,1 个 3 位)都多,所以我们将表头的值设置为 0,如果多出一位来,就暂时存放到表头。递归结束后,如果表头为 1,就在新的链表外再加一个新的表头。/head1 length head2, so M Npublic static int Add(Link head1, Link head2, ref Link newHead, int M, int N)/ got

18、o the endif (head1 = null)return 0;int temp = 0;int result = 0;newHead = new Link(null, 0);if (M N)result = Add(head1.Next, head2, ref newHead.Next, M - 1, N);temp = head1.Data + result;newHead.Data = temp % 10;return temp = 10 1 : 0;else / M = Nresult = Add(head1.Next, head2.Next, ref newHead.Next,

19、 M - 1, N - 1);temp = head1.Data + head2.Data + +result;newHead.Data = temp % 10;return temp = 10 1 : 0;这里假设 head1 比 head2 长,而且 M、N 分别是 head1 和 head2 的长度。12.单链表排序无外乎是冒泡、选择、插入等排序方法。关键是交换算法,需要额外考虑。第7 题我编写了一个交换算法,在本题的排序过程中,我们可以在外层和内层循环里面,捕捉到 pre1 和 pre2,然后进行交换,而无需每次交换又要遍历一次单链表。在实践中,我发现冒泡排序和选择排序都要求内层循环从

20、链表的末尾向前走,这明显是不合时宜的。所以我最终选择了插入排序算法,如下所示:先给出基于数组的算法:代码static intInsertSort(int arr)for(int i=1; i0)public int Pop()if (dataStack.Count = 0)throw new Exception(“The stack is empty“);mindataStack.Pop();return (int)dataStack.Pop();public int Min()if (dataStack.Count = 0)throw new Exception(“The stack is

21、empty“);return (int)mindataStack.Peek();2.设计含 min 函数的栈的另解话说,和青菜脸呆久了,就沾染了上海小市民意识,再加上原本我就很抠门儿,于是对于上一题目,我把一个栈当成两个用,就是说,每次 push,先入站当前元素,然后入栈当前栈中最小元素;pop 则每次弹出 2 个元素。算法代码如下所示(这里最小元素位于当前元素之上,为了下次比较方便):public class NewStackprivate Stack stack;public NewStack()stack = new Stack();public void Push(int elemen

22、t)if (stack.Count = 0)stack.Push(element);stack.Push(element);else if (element stack.Peek)object min = stack.Peek();stack.Push(element);stack.Push(min); public int Pop()if (stack.Count = 0)throw new Exception(“The stack is empty“);stack.Pop();return (int)stack.Pop();public int Min()if (stack.Count =

23、 0)throw new Exception(“The stack is empty“);return (int)stack.Peek();之所以说我这个算法比较叩门,是因为我只使用了一个栈,空间复杂度 o(N),节省了一半的空间(算法 1 的空间复杂度 o(2N)) 。3.用两个栈实现队列实现队列,就要实现它的 3 个方法:Enqueue(入队) 、Dequeue (出队)和Peek(队头) 。1)stack1 存的是每次进来的元素,所以 Enqueue 就是把进来的元素 push到 stack1 中。2)而对于 Dequeue,一开始 stack2 是空的,所以我们把 stack1 中的元

24、素全都 pop 到 stack2 中,这样 stack2 的栈顶就是队头。只要 stack2 不为空,那么每次出队,就相当于 stack2 的 pop。3)接下来,每入队一个元素,仍然 push 到 stack1 中。每出队一个元素,如果 stack2 不为空,就从 stack2 中 pop 一个元素;如果 stack2 为空,就重复上面的操作把 stack1 中的元素全都 pop 到 stack2 中。4)Peek 操作,类似于 Dequeue,只是不需要出队,所以我们调用 stack2 的Peek 操作。当然,如果 stack2 为空,就把 stack1 中的元素全都 pop 到 stac

25、k2 中。5)注意边界的处理,如果 stack2 和 stack1 都为空,才等于队列为空,此时不能进行 Peek 和 Dequeue 操作。按照上述分析,算法实现如下:public class NewQueueprivate Stack stack1;private Stack stack2;public NewQueue()stack1 = new Stack();stack2 = new Stack();public void Enqueue(int element)stack1.Push(element);public int Dequeue()if (stack2.Count = 0

26、)if (stack1.Count = 0)throw new Exception(“The queue is empty“);elsewhile (stack1.Count 0)stack2.Push(stack1.Pop();return (int)stack2.Pop();public int Peek()if (stack2.Count = 0)if (stack1.Count = 0)throw new Exception(“The queue is empty“);elsewhile (stack1.Count 0)stack2.Push(stack1.Pop();return (

27、int)stack2.Peek();4.用两个队列实现栈这个嘛,就要 queue1 和 queue2 轮流存储数据了。这个 “轮流”发生在 Pop 和Peek 的时候,假设此时我们把所有数据存在 queue1 中(此时 queue2 为空) ,我们把 queue1 的 n-1 个元素放到 queue2 中,queue 中最后一个元素就是我们想要pop 的元素,此时 queue2 存有 n-1 个元素(queue1 为空) 。至于 Peek,则是每次转移 n 个数据,再转移最后一个元素的时候,将其计下并返回。那么 Push 的操作,则需要判断当前 queue1 和 queue2 哪个为空,将新元

28、素放到不为空的队列中。public class NewStackprivate Queue queue1;private Queue queue2;public NewStack()queue1 = new Queue();queue2 = new Queue();public void Push(int element)if (queue1.Count = 0)queue2.Enqueue(element);elsequeue1.Enqueue(element);public int Pop()if (queue1.Count = 0 if (queue1.Count 0)while (qu

29、eue1.Count 1)queue2.Enqueue(queue1.Dequeue();/还剩一个return (int)queue1.Dequeue();else /queue2.Count 0while (queue2.Count 1)queue1.Enqueue(queue2.Dequeue();/还剩一个return (int)queue2.Dequeue();public int Peek()if (queue1.Count = 0 int result = 0;if (queue1.Count 0)while (queue1.Count 1)queue2.Enqueue(queu

30、e1.Dequeue();/还剩一个result = (int)queue1.Dequeue();queue2.Enqueue(result);else /queue2.Count 0while (queue2.Count 1)queue1.Enqueue(queue2.Dequeue();/还剩一个result = (int)queue2.Dequeue();queue1.Enqueue(result);return result;5.栈的 push、pop 序列是否一致输入两个整数序列。其中一个序列表示栈的 push 顺序,判断另一个序列有没有可能是对应的 pop 顺序。为了简单起见,我们

31、假设 push 序列的任意两个整数都是不相等的。比如输入的 push 序列是 1、2、3、4、5,那么 4、 5、3、2、1 就有可能是一个pop 系列。因为可以有如下的 push 和 pop 序列:push 1,push 2,push 3,push 4,pop,push 5,pop,pop,pop,pop,这样得到的 pop 序列就是4、5、3、2、1。但序列 4、3、5、1、2 就不可能是 push 序列 1、2、3、4、5的 pop 序列。网上的若干算法都太复杂了,现提出包氏算法如下:先 for 循环把 arr1 中的元素入栈,并在每次遍历时,检索 arr2 中可以 pop 的元素。如果

32、循环结束,而 stack 中还有元素,就说明 arr2 序列不是 pop 序列。static boolJudgeSequenceIsPossible(int arr1,int arr2)Stack stack = new Stack();for (int i = 0, j = 0; i 0 j+;return stack.Count = 0;6.递归反转一个栈,要求不得重新申请一个同样的栈,空间复杂度 o(1)算法思想:汉诺塔的思想,非常复杂,玩过九连环的人都想得通的static void ReverseStack(ref Stack stack)if (stack.Count = 0)ret

33、urn;object top = stack.Pop();ReverseStack(ref stack);if (stack.Count = 0)stack.Push(top);return;object top2 = stack.Pop();ReverseStack(ref stack);stack.Push(top);ReverseStack(ref stack);stack.Push(top2); 7.给栈排个序本题目是上一题目的延伸static void Sort(ref Stack stack)if (stack.Count = 0)return;object top = stack

34、.Pop();Sort(ref stack);if (stack.Count = 0)stack.Push(top);return;object top2 = stack.Pop();if (int)top (int)top2)stack.Push(top);Sort(ref stack);stack.Push(top2);elsestack.Push(top2);Sort(ref stack);stack.Push(top);8如何用一个数组实现两个栈继续我所提倡的抠门儿思想,也不枉我和青菜脸相交一场。网上流传着两种方法:方法 1采用交叉索引的方法一号栈所占数组索引为 0, 2, 4, 6,

35、 8(K*2)二号栈所占数组索引为 1,3,5,7,9 (K*2 + 1)算法实现如下:public class NewStackobject arr;int top1;int top2;public NewStack(int capticy)arr = new objectcapticy;top1 = -1;top2 = -2;public void Push(int type, object element)if (type = 1)if (top1 + 2 = arr.Length)throw new Exception(“The stack is full“);elsetop1 += 2;arrtop1 = element;else /type=2if (top2 + 2 = arr.Length)throw new Exception(“The stack is full“);elsetop2 += 2;arrtop2 = element;public object Pop(int type)object obj = null;if (type = 1)if (top1 = -1)throw new Exception(“The stack is empty“);elseobj = arrtop1;arrtop1 = null;top1 -= 2;

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 高等教育 > 大学课件

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报