1 链表(双向)
每个对象包括一个关键字域和两个指针域prev,next;链表是一种动态的数据结构,其操作需要通过指针进行。链表内存的分配不是在创建链表时一次性完成,而是每添加一个节点就分配一次内存。由于没有闲置内存。他的空间效率比数组更高。
相对于数组,长度可变;插入删除更容易。
(单向链表):从头插 从尾插
public class LinkList {
Node first;
public class Node{
public int iData;
public double dData;
public Node next;
public Node(int id, double dd)
{ this.iData = id;
this.dData = dd; }
public void displayLink() {
System.out.println("{" + iData + "," + dData + "}"); }
}
LinkList(){
first=null;
}
public void addfirst(int id, double dd){
Node node=new Node(id,dd);
node.next=first;
first=node;
}
public void addlast(int id, double dd){
Node node = new Node(id, dd); // 将新链接点的关系子段 next 指向旧的首链接点。
Node p = first;
// 注意链表为空的时候的插入
if(first==null){
first = node;
}
// 尾插法
else{
while(p.next != null){
p = p.next;
}
p.next = node;
}
}
public Node deletefirst(){
Node p=first.next;
first=p;
return first;
}
public Node deletelast(){
Node p=first.next;
first=p;
return first;
}
public Node select(int key){
Node current=first;
while(current.iData!=key){
if(current.next!=null){
current=current.next;
}
else { System.out.println("没有找到数据");
current = null;
break; }
}
return current;
}
public void delectelike(int key){
Node current=first;
Node previous=first;
boolean isfind=true;
while(current.iData!=key){
if(current.next!=null){
current=current.next;
previous=current;
}
else{System.out.println("没有找到数据");
current = null;
isfind = false;
}
}
if(isfind==true){
if(current==first){
first=first.next;
}
else {previous.next=current.next;}
}
{
System.out.println("no find the Data of delete");
}
}
public Node displayList() {
System.out.println("List (fist - -> last )");
Node top = first;
while (top != null) {
// top.displayLink();
System.out.println("{" + top.iData + "," + top.dData + "}");
top = top.next; }
return top;
}
public boolean isEmpty() { return (first == null); }
/**
* @param args
*/
public static void main(String[] args) {
LinkList l = new LinkList(); // 增加
l.addfirst(1, 234);
l.addfirst(2, 332);
l.addfirst(3, 32);
l.displayList(); // 查询
Node findLink = l.select(2);
if (findLink != null) {
System.out.println(findLink.dData); } // 删除
l.delectelike(2);
l.displayList();
}
}
(双向链表)
代码:
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. 从尾到头打印链表
题目:输入一个链表的头节点,从尾到头打印出来每个节点的值。
解法:遍历,每遍历到的元素存到栈,然后输出栈即可。
代码:
public Node displayList() {
System.out.println("List (fist - -> last )");
Node top = first;
while (top != null) {
// top.displayLink();
System.out.println("{" + top.iData + "}");
top = top.next; }
return top;
}
2. O(1)
时间删除链表节点
题目:给定单链表的头指针和一个节点指针,O(1)
时间删除该链表节点
解法:下一个节点的内容复制到需要删除的点,即覆盖。然后删该店的下一个节点。
这里需要考虑两个边界条件,1 要删除的点位于尾部 2 链表只有一个节点
要考虑鲁棒性。
代码:
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个。(这种一次遍历,对时间要求比较高的程序,就需要借助空间,再开辟一个指针)
要考虑鲁棒性。
代码:
//遍历链表一次,删除倒数第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 指针时,需要首先将其的后继节点保存下来。)
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;
}
}