常见数据结构面试题

1、数组和链表的区别。
从逻辑结构来看:
数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况,即数组的大小一旦定义就不能改变。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费;链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删除数据项时,需要移动其它数据项)。  
从内存存储来看:
(静态)数组从栈中分配空间(用NEW创建的在堆中), 对于程序员方便快速,但是自由度小;链表从堆中分配空间, 自由度大但是申请管理比较麻烦.
从访问方式来看:
数组在内存中是连续存储的,因此,可以利用下标索引进行随机访问;链表是链式存储结构,在访问元素的时候只能通过线性的方式由前到后顺序访问,所以访问效率比数组要低。

区别:
数组静态分配内存,链表动态分配内存; 
数组在内存中连续,链表不连续; 
数组元素在栈区,链表元素在堆区; 
数组利用下标定位,时间复杂度为O(1),链表定位元素时间复杂度O(n); 
数组插入或删除元素的时间复杂度O(n),链表的时间复杂度O(1)。


2、反转单链表

给一个单向链表,把它从头到尾反转过来。比如: a -> b -> c ->d 反过来就是 d -> c -> b -> a 。

这里讲解两种方法:

第一种方法就是把每个Node按照顺序存入到一个stack里面,这样,最后面一个就在最上面了。然后,把每一个再取出来,这样顺序就换过来了。

[java] view plaincopy
  1. public static Node reverse(Node head) {  
  2.     Stack<Node> stack = new Stack<Node>();  
  3.       
  4.     // put all the nodes into the stack  
  5.     while (head != null) {  
  6.         stack.add(head);  
  7.         head = head.next();  
  8.     }  
  9.       
  10.     //reverse the linked list  
  11.     Node current = stack.pop();  
  12.     head = current;  
  13.     while (stack.empty() != true) {  
  14.         Node next = stack.pop();  
  15.         //set the pointer to null, so the last node will not point to the first node.  
  16.         next.setNext(null);  
  17.         current.setNext(next);  
  18.         current = next;  
  19.     }  
  20.       
  21.     return head;      
  22. }  

第二种方法就是利用两个指针,分别指向前一个节点和当前节点,每次做完当前节点和下一个节点的反转后,把两个节点往下移,直到到达最后节点。

[java] view plaincopy
  1. public static Node reverse(Node head) {  
  2.     Node previous = null;  
  3.   
  4.     while (head != null) {  
  5.         Node nextNode = head.next();  
  6.         head.setNext(previous);  
  7.         previous = head;  
  8.         head = nextNode;  
  9.     }  
  10.           
  11.     return previous;      
  12. }  

3、链表存在环路的判断

给定一个有环链表,实现以算法返回环路的开头结点。

有环链表的定义

在链表中某个节点的next元素指向它前面出现过的节点,则表明该链表存在环路。

示例

输入:A->B->C->D->E->C(C节点出现两次)

输出:C

分析

1,快慢指针法判断链表是否有环

fast每次前移两步,slow每次前移一步,两指针若能相遇,则有环,否则没有环。

2,假设链表头到环头移动k步,slow指向环头的时候,fast移动了2*k步,此时两者相距k步,也可以认为快指针再过m*size-k步之后追上慢指针。当两者相遇的时候,则慢指针距离环头还有k步。因为此时不知道k的具体大小,但是知道k是链表头到环头的步数,让fast指向链表头,之后快慢指针每次往后移动一步,两者相遇的地方就是环头。

[java] view plaincopy
  1. package test;  
  2.   
  3. public class FindLoopStart {  
  4.     public Node findLoopStart(Node head){  
  5.         Node fast = head;  
  6.         Node slow = head;  
  7.         while(fast!=null || fast.next!=null){  
  8.             fast = fast.next.next;  
  9.             slow = slow.next;  
  10.             if(fast == slow)  
  11.                 break;  
  12.         }  
  13.         //没有环则返回null  
  14.         if(fast==null || fast.next==null)  
  15.             return null;  
  16.         //相遇之后,slow节点再走k步达到环开头  
  17.         //此时,并不知道k的具体值,但是知道k是从链表开头到环头的步数  
  18.         //于是,让fast指向链表头,每次往后移一步,则再次相遇的时候,走的步数就是k  
  19.         //则相遇地点就是环的开头  
  20.         fast = head;  
  21.         while(fast != slow){  
  22.             fast = fast.next;  
  23.             slow = slow.next;  
  24.         }  
  25.         return slow;  
  26.     }  
  27. }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值