虽是读书笔记,但是如转载请注明出处 http://segmentfault.com/blog/exploring/
.. 拒绝伸手复制党
以下是算法导论第十章的学习笔记
1 栈
栈顶指针 top (初始值top = -1)指向栈顶元素,插入时先修改指针再插入,删除时先取栈顶元素再修改指针.
1.1 性质
后进先出
入栈,出栈都是O(1)
1.2 核心代码
java
public class Stack { private int[] array = new int[5]; private int top = -1; public Boolean stackempty(){ if(top == -1){ return true; } else return false; } public void push(int x){ if(top<=array.length-1){ array[++top] = x; } else{ System.out.println("overflow"); } }
java
public int pop() { int number = -1; if(stackempty() != true){ number = array[top]; top--; return number; } else { System.out.println("underflow"); return -1; } }
2 队列
用array[n]
数组实现的至多含有n-1
个元素的队列的方法.
队列具有属性head[Q]
,指向队列的头. 属性tail[Q]
指向新元素要被插入的地方
head[Q] = tail[Q]
时,队列空;head[Q] = tail[Q]+1
时,队列满;
初始head[Q] = tail[Q] = 1
2.1 性质
先进先出
入队,出队都是O(1)
2.2 核心代码
java
public class Queue { private int[] array = new int[4]; private int head = 1; private int tail = 1; //入队 public void enqueue(int x){ //处理上溢 try{ if(head != tail +1){ array[tail++] = x; if(tail == array.length){ tail = 0; } } else{ System.out.println("overflow"); throw new Exception("queue overflow"); } }catch(Exception e){ e.printStackTrace(); } }
java
//出队 public int dequeue(){ int number=0; try{ if(tail != head){ number = array[head]; head++; } else{ throw new Exception("queue underflow"); } }catch(Exception e){ System.out.println("underflow"); e.printStackTrace(); } return number; }
3. (双向)链表
链表是面试时被频繁提及的DS。 每个对象包括一个关键字域和两个指针域prev,next;
链表为什么这么受欢迎?
链表是一种动态的数据结构,其操作需要通过指针进行。链表内存的分配不是在创建链表时一次性完成,而是每添加一个节点就分配一次内存。由于没有闲置内存。他的空间效率比数组更高。
通过使用哨兵nil节点(增加一个nil节点)可以简化边界条件。
3.1 性质
相对于数组,长度可变;插入删除更容易。
3.2核心代码
(单向链表)
java
public class LinkedList { private Node head; private Stack s; LinkedList(){ head = null; s = new Stack(); } private static class Node{ int item; Node next; Node(){ this.item = 0; this.next = null; } Node(int item, Node next){ this.item = item; this.next = next; } } public void insert(int x){ Node node = new Node(x, null); Node p = head; // 注意链表为空的时候的插入 if(head==null){ head = node; } // 尾插法 else{ while(p.next != null){ p = p.next; } p.next = node; } } public void travese(Node head){ Node p = head; while(p!=null){ System.out.println(p.item); p = p.next; } }
(双向链表)
java
public class DoubleLinkedList { // 哨兵节点nil(作为表头指针) private Node nil; // 初始化一个链表 DoubleLinkedList(){ nil = new Node(); nil.next = nil; nil.prev = nil; count = 0; } // 链表长度 private int count; private static class Node{ int item; Node next; Node prev; Node(){ item = 0; next = null; prev = null; } Node(int item, Node next, Node prev){ this.item = item; this.next = next; this.prev = prev; } } //返回当前链表的长度 public int length(){ return count; } //获取value为k的节点 public Node listsearch(int k){ Node result = null; Node head = nil; while(head.next != nil){ if(head.next.item == k){ result = head.next; break; } else head = head.next; } return result; } //插入一个节点在队首 public void listinsert(Node node){ node.next = nil.next; nil.next.prev = node; nil.next = node; node.prev = nil; count++; } //根据value删除一个节点 public Node listdelete(Node node){ Node head = nil; Node nodetodelete; while(head.next!=nil){ if(head.next.item == node.item){ nodetodelete = head.next; //将要删除的节点 head.next = nodetodelete.next; nodetodelete.next.prev = head; nodetodelete = null; count--; } else{ head = head.next; } } return node; } //输出链表 public void traverse(){ Node head = nil; while( head.next!= nil){ System.out.println(head.next.item); head = head.next; } }
3.3 扩展
1. 从尾到头打印链表
题目:输入一个链表的头节点,从尾到头打印出来每个节点的值。
解法:遍历,每遍历到的元素存到栈,然后输出栈即可。
代码:
java
public void reveaseoutput(Node head){ Node p = head; if(p==null){ System.out.println("empty list"); } while(p != null){ s.push(p.item); p = p.next; } while(s.stackempty()!= true){ int n = s.pop(); System.out.println(n); } }
2. O(1)
时间删除链表节点
题目:给定单链表的头指针和一个节点指针,O(1)
时间删除该链表节点
解法:下一个节点的内容复制到需要删除的点,即覆盖。然后删该店的下一个节点。
这里需要考虑两个边界条件,1 要删除的点位于尾部 2 链表只有一个节点
要考虑鲁棒性。
代码:
java
public void deleteNode(Node head, Node pToDeleted){ if(head!=null){ //要删除的是尾节点 if(pToDeleted.next == null){ //如果要删除的是链表唯一的节点 if(head.next==null){ head = null; System.out.println(head); pToDeleted = null; } else{ Node p = head; while(p.next!=pToDeleted){ p = p.next; } p.next = null; pToDeleted =null; } } //要删除的不是尾节点,且节点数大于1 else{ pToDeleted.item = pToDeleted.next.item; pToDeleted.next = pToDeleted.next.next; pToDeleted = null; } }else{ System.out.println("the linklist is empty"); } }
3. 倒数第k个节点
题目:输入一个链表,输出该链表第倒k个节点。(链表从1开始计数)
解法:定义两个指针,第一个指针从链表的head指针开始遍历,向前走k-1步的时候,第二个指针开始和它一起走。当第一个指针的next指向null的时候,第二个指针指向了倒数第k个。(这种一次遍历,对时间要求比较高的程序,就需要借助空间,再开辟一个指针)
要考虑鲁棒性。
代码:
java
//遍历链表一次,删除倒数第K个元素 public Node FindKthToTail(Node head, int k){ Node p=head; Node q = head; int i; if(head==null || k==0){ return null; } for(i=0;i<k-1;i++){ if(p.next !=null){ p = p.next; } else{//当k大于链表的长度的时候 System.out.println("error k"); return null; } } while(p.next!=null){ p = p.next; q = q.next; } return q; }
4. 反转链表
题目:定义一个函数,输入链表头节点,反转该链表并输出反转后链表的头节点。
解法:借助三个指针,prev, p, next. 避免指针断裂。
( 为了正确的反转一个链表,需要调整链表中指针的方向【指针反向】。注意,在单链表中,将一个节点的指向后继的指针指向它的前驱,将会导致链表的断裂。导致无法在单链表中遍历它的后继节点,因此,在调整某一节点的 next 指针时,需要首先将其的后继节点保存下来。)
代码:
java
public Node reverse(){ Node p = head; try{ if(p!=null){ Node pnext = p.next; p.next = null; while(pnext!= null){ Node r = pnext.next; pnext.next = p; p = pnext; pnext = r; } }else{ throw new Exception("empty list"); } }catch(Exception e){ e.printStackTrace(); }finally{ return p; } }
5 合并链表
题目:输入两个增序的链表,合并这两个链表并使新链表仍然增序。
解法:重点强调鲁棒性:两个链表一个或者两个都是null,两个链表只有一个节点。
代码:
java
public Node merge(Node head1, Node head2){ if(head1 == null) { return head2; } if(head2 == null){ return head1; } else{ Node newhead; Node r; Node p = head1; Node q = head2;
java
if(head1.item <= head2.item){ newhead = head1; p = p.next; } else{ newhead = head2; q = q.next; } r = newhead; while(p!=null && q!=null){ if(p.item <= q.item){ r.next = p; p = p.next; r = r.next; } else{ r.next = q; q = q.next; r = r.next; } } if(p!=null){ r.next = p; } if(q!=null){ r.next = q; } return newhead; } }
6 复杂链表的复制
题目:
解法:
代码:
java
//1. 根据原始链表的每个节点创建对应的copy节点 public void CloneNode(ComplexNode head){ ComplexNode p = head; while(p!=null){ ComplexNode node = new ComplexNode(p.item,null,null); node.next = p.next; p.next = node; p = node.next; } }
java
//2. 设置复制出来的节点的sibling public void connectsiblingnodes(ComplexNode head){ ComplexNode p = head; while(p!=null){ ComplexNode q = p.next; if(p.sibling!=null) { q.sibling = p.sibling.next; } p = q.next; } }
java
//3. 拆分链表 public ComplexNode ReconnectNodes(ComplexNode head){ ComplexNode p = head; if(p!=null){ ComplexNode newhead = p.next; ComplexNode q = newhead; while(q.next!=null){ p.next = q.next; p = q.next; q.next = p.next; q = p.next; } return newhead; } else{ return null; } }
7 寻找第一个公共节点
题目:输入两个链表,找出第一个公共节点。
解法:Y形
代码:
java
public Node findfirstcommonnode(Node head1, Node head2){ Node p = head1;Node q = head2; int length = int length1 = int length2= 0; while(p!=null){ length1 = length1 + 1; p = p.next; } while(q!=null){ length2 = length2 + 1; q = q.next; } p = head1;q = head2; if(length1>length2){ length = length1 - length2; while(length>0){ p = p.next; length--; } } if(length1<length2){ length = length2 - length1; while(length>0){ q = q.next; length--; } } while(p!=null&&q!=null&&p.item != q.item){ p = p.next; q = q.next; } return p; }
想更一进步的支持我,请扫描下方的二维码,你懂的~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。