1、数组和链表的区别。
从逻辑结构来看:
数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况,即数组的大小一旦定义就不能改变。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费;链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删除数据项时,需要移动其它数据项)。
从内存存储来看:
(静态)数组从栈中分配空间(用NEW创建的在堆中), 对于程序员方便快速,但是自由度小;链表从堆中分配空间, 自由度大但是申请管理比较麻烦.
从访问方式来看:
数组在内存中是连续存储的,因此,可以利用下标索引进行随机访问;链表是链式存储结构,在访问元素的时候只能通过线性的方式由前到后顺序访问,所以访问效率比数组要低。
区别:
数组静态分配内存,链表动态分配内存;
数组在内存中连续,链表不连续;
数组元素在栈区,链表元素在堆区;
数组利用下标定位,时间复杂度为O(1),链表定位元素时间复杂度O(n);
数组插入或删除元素的时间复杂度O(n),链表的时间复杂度O(1)。
2、反转单链表
给一个单向链表,把它从头到尾反转过来。比如: a -> b -> c ->d 反过来就是 d -> c -> b -> a 。
这里讲解两种方法:
第一种方法就是把每个Node按照顺序存入到一个stack里面,这样,最后面一个就在最上面了。然后,把每一个再取出来,这样顺序就换过来了。
- public static Node reverse(Node head) {
- Stack<Node> stack = new Stack<Node>();
- // put all the nodes into the stack
- while (head != null) {
- stack.add(head);
- head = head.next();
- }
- //reverse the linked list
- Node current = stack.pop();
- head = current;
- while (stack.empty() != true) {
- Node next = stack.pop();
- //set the pointer to null, so the last node will not point to the first node.
- next.setNext(null);
- current.setNext(next);
- current = next;
- }
- return head;
- }
第二种方法就是利用两个指针,分别指向前一个节点和当前节点,每次做完当前节点和下一个节点的反转后,把两个节点往下移,直到到达最后节点。
- public static Node reverse(Node head) {
- Node previous = null;
- while (head != null) {
- Node nextNode = head.next();
- head.setNext(previous);
- previous = head;
- head = nextNode;
- }
- return previous;
- }
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指向链表头,之后快慢指针每次往后移动一步,两者相遇的地方就是环头。
- package test;
- public class FindLoopStart {
- public Node findLoopStart(Node head){
- Node fast = head;
- Node slow = head;
- while(fast!=null || fast.next!=null){
- fast = fast.next.next;
- slow = slow.next;
- if(fast == slow)
- break;
- }
- //没有环则返回null
- if(fast==null || fast.next==null)
- return null;
- //相遇之后,slow节点再走k步达到环开头
- //此时,并不知道k的具体值,但是知道k是从链表开头到环头的步数
- //于是,让fast指向链表头,每次往后移一步,则再次相遇的时候,走的步数就是k
- //则相遇地点就是环的开头
- fast = head;
- while(fast != slow){
- fast = fast.next;
- slow = slow.next;
- }
- return slow;
- }
- }