一、单链表
1.不带头节点的单链表:
主体:
public class LianBiaowithoutHead <E> {
protected Node<E> head; //声明第一个结点(并不是头结点)
class Node<E>{ //创建内部类Node
protected E element;
protected Node<E> next;
public Node(E data){
this.element = data;
}
}
}
方法:
不带头结点的单链表在进行操作时,移动指针语句:tmp = tmp.next需要写在操作语句之后,因为head结点保存着第一个结点的元素,不可以跳过它。
增删改查等代码较简单不放上来了,这里放一个show方法做代表。
public void show(){
Node<E> tmp = head;
while(tmp != null){
System.out.print(tmp.element+" ");
tmp = tmp.next; //注意在操作语句之后移动tmp,否则会跳过第一个结点,而这里的head是放有数据的
}
System.out.println();
}
2.带头结点的单链表:
主体:
public class LianBiaowithHead <E>{
protected Node <E> head;
class Node<E>{
protected E element;
protected Node<E> next;
public Node(E data){
this.element = data;
}
}
public LianBiaowithHead(){ //需要创立头结点
head = new Node<>((E)new Object());
}
}
方法:
带头结点的单链表在进行删除等操作时,移动指针语句:tmp = tmp.next需要写在操作语句之前,因为head结点的数据域没有东西,需要跳过它再进行打印等操作。
依然放一个show方法做代表。
public void show(){
Node<E> tmp = head;
while(tmp.next != null){
tmp = tmp.next; //跳过头结点打印
System.out.print(tmp.element + " ");
}
System.out.println();
}
3.循环链表
主体:
public class CircleList<E> {//循环单链表(带头结点)
protected Node<E> head;
class Node <E>{
protected E element;
protected Node<E> next;
public Node(E data){
this.element = data;
}
}
public CircleList(){
this.head = new Node<>((E)new Object());
head.next = head;
}
方法:
和上面的2,3一样,记得将最后一个结点的指针域设成head,遍历的循环条件变成 tmp.next != head
以add方法作代表:
public void addTail(E data){
Node<E> n = new Node<E>(data);
Node<E> tmp= head;
while(tmp.next != head){
tmp = tmp.next;
}
n.next = tmp.next;
tmp.next = n;
}
4.单链表的一些其他基本方法
(1)【合并两个有序链表,合并后依然有序】:
思想:
先比较list1,list2的第一个结点元素的大小,小的作合并后newList的头结点,以两个链表的表头同时不为空作循环条件,陆续比较元素大小,小的跟在新表指针后,同时该旧表头往后移动,另一个不动。也就是一个if else语句。
代码实现:
public <E extends Comparable<E>> Node<E> mergeList(Node<E> head1,Node<E> head2){
Node<E> tmp = null;
Node<E> curHead;
if(head1.element.compareTo(head2.element) <0){
curHead = head1;
head1 = head1.next; //别忘了这个的表头要往后移一个
}
else {
curHead = head2;
head2 = head2.next;
}
tmp = curHead;
while(head1!= null && head2!= null){
if(head1.element.compareTo(head2.element)<0){
tmp.next =head1;
head1 = head1.next;
}
else{
tmp.next = head2;
head2 = head2.next;
}
tmp = tmp.next;
}
if(head1 == null){
tmp.next = head2;
}
if(head2 == null){
tmp.next = head1;
}
return curHead;
}
tips1:
因为要使用compareTo()方法比较element,所以要在方法声明时在T后extends Comparable,也就是在说:擦除的时候不用擦的那么彻底,擦到还有Comparable,能让我使用它的方法~
tips2:
自己在写代码的时候一遇到泛型就有点乱,不知道怎么写方法声明的顺序了,正确顺序应该是:pubic <T> 返回值 方法名(){}
(2)【逆序输出链表元素】:
思想:
用递归可以简单实现。运行时一层层的进reversePrint方法,每一层都没有走到底,直到最后一层(head指针为空)进入if语句后return,这才返回上一层继续往下走进行打印数据,这一层的方法才算结束,正常return后再往下走打印数据……,直到return到最初进入的一层。最后输出就是逆序的。
代码实现:
public static <E> void reversePrint(LianBiaowithoutHead<E>.Node<E> head){
if(head == null)
return;
reversePrint(head.next);
System.out.print(head.element + " ");
}
(3)【逆置链表】:
思想:
设置三个指针,也就是除了普通临时指针cur之外,另外设置一个在当前指针之前的指针p,一个在当前指针之后的指针next,以当前指针不空为条件进行循环,每一次循环都会将p,cur之间的指向调转方向。
代码实现:
public Node<E> reverseList(Node<E> head){
Node <E> cur = head;
Node <E> p = null;
Node <E> newHead =null;
while(cur!= null){
Node<E> next = cur.next;
if(next == null)
newHead = cur;
cur.next = p;
p = cur;
cur = next;
}
return newHead;
}
(4)【查找单链表中倒数第k个结点】:
思想:
倒数第k个结点和最后一个结点下标差k-1-,所以可以设立两个指针,一个指针p1先走k-1步,再和另一个起点在head的指针p2同时前进,当p1.next为空时,p2也就走到了倒数第k个结点。
看图:
代码实现:
public E findTheK(int index){
if(head == null || index<0)
return null;
Node<E> p1 = head;
Node<E> p2 = head;
int num = 0;
while (num != index-1){
p2 = p2.next;
num++;
}
while(p2.next != null){
p1 = p1.next;
p2 = p2.next;
}
return p1.element;
}
<