上篇我对BigInteger 膜拜了一番,以为很复杂呢,结果看jdk带的源码,也就一个3000行java文件而已。因而右必要自己亲自写一些底层的东西。
一来是为了练手,二来是为了体会java的思维方式,三是比较C++的后继者们扬弃了什么。
废话不说,要点都写在注释里面了:
/*LinkedListTest.java --模仿C风格的双链表
1. head->func(head) 通过this变成head.func()。
2. C宏/模板 --> 泛型,在方法头上加
public <T extends Comparable<? super T>>
public <T extends Comparable<T>>
虽然可以编译,但是不能调用这个类的其他方法。
通过搜索keywords:"Comparable generic type"
正确的办法是在泛型类定义时
DNode<T> 改成 DNode<T extends Comparable<T>>
extends 表示限定。
* author ludi 2014.03*/
class DNode<T extends Comparable<T>> {
public T val;
public DNode<T> next, prev;
/*链表通常是双向的, 并且使得某些操作更简单*/
public DNode(){
/*创建链表头一般val置为空,当然我们也可以拿来当作链表个数*/
val = null;
next = prev = this;
}
public DNode(T item){
val = item;
next = prev = this;
/*这里为什么不是null?
* 目的是形成一个闭环,设链表最后一个节点是tail,
* 则head->prev = tail; tail.next = head.
* 闭环的优点是使后面的插入删除的操作不必判断指针是否为空*/
}
/*因为参数是泛型<T>,只好把链表所有方法都放在DNode<T>里面了*/
public String toString(){
String str = "[";
/*遍历模板:计数,查找,...*/
DNode<T> curr = this.next;
while(curr != this){
if(curr.val != null)str += curr.val.toString() + ", ";
curr = curr.next;
}
str += "]";
return str;
}
public DNode<T> indexOf( int idx){
DNode<T> curr = this.next;
for(; curr != this; curr = curr.next){
if(0 == idx)break;
--idx;
}
return (0 == idx) ? curr : null;
}
public void insertAfter(DNode<T> node, DNode<T> x){
x.next = node.next;
node.next.prev = x;
x.prev = node;
node.next = x;
}
public void insertTail( DNode<T> x){
insertAfter(this.prev, x);
}
public DNode<T> deleteAfter(DNode<T> node){
DNode<T> x = node.next;
x.next.prev = node;
node.next = x.next;
return x;
}
/*上面的几个方法是基本操作,对于用作一般链表,链表形式的栈,队列已经足够了。
* 下面的仅仅是练习题目*/
public void reverseList(){
/*链表翻转, 类似于数组翻转。(如果是单链表,如何?)*/
DNode<T> first = this.next, last = this.prev;
while(first != last){
T val = first.val;
first.val = last.val;
last.val = val;
first = first.next;
last = last.prev;
}
}
public void deleteAll(){
/*清空链表*/
DNode<T> x = null;
while(this.next != this){
x = deleteAfter(this);
x = null;
}
}
public DNode<T> insertionSort()
{/*递增排序*/
DNode<T> list = new DNode<T>(), j = null, x = null;
DNode<T> head = this;
x = head.deleteAfter(head);
for(; x != head; x = head.deleteAfter(head)){
/*<= 导致不稳定排序。查找如果从前往后就不能体现插入排序的优势了*/
j = list.prev;
while(j != list && x.val.compareTo(j.val) < 0){
j = j.prev;
/*如果是数组,这里边移位arr[j+1] = arr[j]。
* 由此看出,只有sizeof(T)大于计算机字长时,链表形式的才更快*/
}
System.out.println(list.toString() + " <-- " + x.val + " at " + j.val);
list.insertAfter(j, x);
}
return list;
}
public int search( T val){
/*如果没有找到返回-1*/
DNode<T> first = this.next;
int idx = 0;
for(; first != this; first = first.next){
if(0 == first.val.compareTo(val))return idx;
++idx;
}
System.out.printf("===xx\n"); /*这个为什么没有打出来?A: 不要这样调println(search(i));*/
return -1;
}
}
public class LinkedListTest{
public static void main(String[] arg){
/*创建链表头,节点引用*/
DNode<Integer> head = new DNode<Integer>(), node = null, curr = null;
int i;
for(i = 0; i < 5; ++i){
node = new DNode<Integer>(2*i);
head.insertTail(node);
}
System.out.println(head.toString() );
for(i = 0; i < 5; ++i){
node = new DNode<Integer>(2*i+1);
head.insertAfter(head, node);
}
System.out.println(head.toString() );
i = 5;
node = head.indexOf(i);
head.deleteAfter(node);
System.out.printf("delete afert %d-th %d:\n", i, node.val);
System.out.println(head.toString() );
head.reverseList();
System.out.println("reversed:\n" + head.toString());
node = head.insertionSort();
head = null; head = node; /*防内存泄露, 编译器会不会自动搞?*/
System.out.println("Sorted:\n" + head.toString());
i = 6;
System.out.printf("Search %d at pos %d\n", i, head.search( i));
head.deleteAll();
System.out.println("deleteAll:\n" + head.toString());
head = null;
}
}
/*
ludi@ubun:~/java
$ javac -encoding UTF-8 LinkedListTest.java && java LinkedListTest
[0, 2, 4, 6, 8, ]
[9, 7, 5, 3, 1, 0, 2, 4, 6, 8, ]
delete afert 5-th 0:
[9, 7, 5, 3, 1, 0, 4, 6, 8, ]
reversed:
[8, 6, 4, 0, 1, 3, 5, 7, 9, ]
Sorted:
[0, 1, 3, 4, 5, 6, 7, 8, 9, ]
Search 6 at pos 5
deleteAll:
[]
ludi@ubun:~/java
$
*/