链表作为最进本的数据结构,在程序设计中有着非常重要的作用,其存储特点如下:可以用任意一组存储单元来存储单链表中的数据元素(存储单元可以是不连续的),而且,除了存储每个数据元素的值以外,还必须存储指示其直接后继元素的信息,这两部分信息组成的数据元素的存储映像称为结点。N个结点链在一块被称为链表,当结点只包含其后继结点的信息的链表就被称为单链表。
单链表有带头结点和不带头结点之分:
不带头结点
带头结点
eg1:基本操作
先定义如下数据类来存储结点信息
public class Node {
Node next=null;
int data;
public Node(){};
public Node(int data){
this.data=data;
}
}
package linkedlist;
public class MyLinkedList {
Node head=null;//链表头,不带头结点
public void addNode(int d){//向链表中插入数据
Node newNode=new Node(d);
if(head==null){
head=newNode;
return;
}
Node tmp=head;
while(tmp.next!=null){
tmp=tmp.next;
}
tmp.next=newNode;
}
public Boolean deleteNode(int index){//删除第index个结点
if(index<1||index>length()){
return false;
}
if(index==1){
head=head.next;
return true;
}
int i=2;
Node preNode=head;
Node curNode=preNode.next;
while(curNode!=null){
if(i==index){
preNode.next=curNode.next;
return true;
}
preNode=curNode;
curNode=curNode.next;
i++;
}
return true;
}
public int length(){//返回长度
int length=0;
Node tmp=head;
while(tmp!=null){//tmp.next!=null不一样
length++;
tmp=tmp.next;
}
return length;
}
public Node orderList(){//对链表进行排序,返回排序后的头结点
Node nextNode=null; //选择排序
int temp=0;
Node curNode=head;
while(curNode!=null){
nextNode=curNode.next;
while(nextNode!=null){//第一个和剩下的进行比较获得min
if(curNode.data>nextNode.data){
temp=curNode.data;
curNode.data=nextNode.data;
nextNode.data=temp;
}
nextNode=nextNode.next;
}
curNode=curNode.next;
}
return head;
}
public void printList(){
Node tmp=head;
while(tmp!=null){
System.out.println(tmp.data);
tmp=tmp.next;
}
}
public static void main(String [] args){
MyLinkedList list=new MyLinkedList();
list.addNode(3);
list.addNode(1);
list.addNode(5);
list.addNode(3);
list.printList();
System.out.println(list.length());
list.deleteNode(1);//数的是个数,删除第一个,不是索引0
System.out.println("listLen="+list.length());
System.out.print("before order:");
list.printList();
list.orderList();
System.out.print("after order:");
list.printList();
}
}
eg2:从链表中删除重复数据
package linkedlist;
import java.util.*;
/*
* 从链表中删除重复数据
*/
public class DeleteDuplecate {
/*
* 遍历链表,把遍历到的值存储到一个Hashtable中,在遍历过程中,若当前访问的值
* 在Hashtable中已经存在,说明这个数据是重复的,可以删除
* 时间复杂度低,但是在遍历过程中需要额外的存储空间来保存已遍历过的值
*/
public static void deleteDuplecate(Node head){
Hashtable<Integer,Integer> table=new Hashtable<Integer,Integer>();
Node tmp=head;
Node pre=null;
while(tmp!=null){
if(table.containsKey(tmp.data)){
pre.next=tmp.next;
}
else{
table.put(tmp.data, 1);
pre=tmp;
}
tmp=tmp.next;
}
}
/*
* 双重循环遍历,外循环正常遍历链表
* 假设外循环当前遍历的结点为cur,内循环从cur开始遍历,若碰到与cur所指向结点值相同
* 则删除这个重复结点
* 不需要额外的存储空间,但时间复杂度比上面高
*/
public static void deleteduplecate(Node head){
Node p=head;
while(p!=null){
Node q=p;
while(q.next!=null){
if(p.data==q.next.data){
q.next=q.next.next;
}
else{
q=q.next;
}
}
p=p.next;
}
}
public static void print(Node head){
while(head!=null){
System.out.print(head.data+" ");
head=head.next;
}
System.out.println();
}
public static void main(String[] args){
int[] num={2,4,3,3,5};
Node head=new Node();
Node pre=head;
for(int i=0;i<num.length;i++){
Node node=new Node(num[i]);
pre.next =node;
pre=node;
}
print(head.next);
deleteduplecate(head);
// deleteDuplecate(head);
print(head.next);
}
}
eg3:找出单链表中倒数第k个元素
package linkedlist;
/*
* 找出单链表中倒数第k个元素
* 方法一:先遍历一边单链表,求出长度n,然后将倒数第k个转为正数n-k个
* 再遍历一次即可得到,问题,需要对链表进行两次遍历
* 方法二:
* 设置两个指针,其中一个比另一个先前移k-1步,然后两个指针同时前移
* 先行的指针值为null时另一个指针所指的位置就是要找的位置
*/
public class FindElem {
public static Node findElem(Node head, int k){
if(k<1){
return null;
}
Node p1=head;
Node p2=head;
for(int i=0;(i<k-1 )&& (p1!=null);i++){//p1先前移k-1
p1=p1.next;
}
if(p1==null){
System.out.println("k is illegal");
return null;
}
while(p1.next!=null){//p1,p2同前移
p1=p1.next;
p2=p2.next;
}
return p2;
}
public static void main(String [] args){//简单测试,输出为1
int[] num={2,4,1,3,5,6};
Node head=new Node();
Node pre=head;
for(int i=0;i<num.length;i++){
Node node=new Node(num[i]);
pre.next =node;
pre=node;
}
Node result=findElem(head,4);
System.out.println(result.data);
}
}
eg4:从尾到头输出单链表
package linkedlist;
/*
* 方法一:链表中链接结点的指针反转改变链表方向,然后从尾到头输出
* 缺点:需要额外的操作
* 方法二:遍历链表,每经过一个结点,就把该结点放到一个栈中
* 遍历完后,从栈顶开始输出
* 缺点:需要维护一个额外的栈空间
* 方法三:递归,每访问一个结点,先递归输出它后面的结点,再输出该结点自身
*/
public class PrintListReversely {
public static void printListReversely(Node pListHead){
if(pListHead!=null){
printListReversely(pListHead.next);
System.out.println(pListHead.data);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Node node1=new Node(2);
Node node2=new Node(4);
Node node3=new Node(1);
node1.next=node2;
node2.next=node3;
printListReversely(node1);
}
}