链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。
单向链表的各种操作(Java实现)
import java.util.HashMap;
import java.util.Scanner;
import java.util.Stack;
/**
*
* @author kerryfish
* 关于java中链表的操作
* 1. 求单链表中结点的个数: getListLength
* 2. 将单链表反转: reverseList(遍历),reverseListRec(递归)
* 3. 查找单链表中的倒数第K个结点(k > 0): reGetKthNode
* 4. 查找单链表的中间结点: getMiddleNode
* 5. 从尾到头打印单链表: reversePrintListStack,reversePrintListRec(递归)
* 6. 已知两个单链表pHead1 和pHead2 各自有序,把它们合并成一个链表依然有序: mergeSortedList, mergeSortedListRec
* 7. 对单链表进行排序,listSort(归并),insertionSortList(插入)
* 8. 判断一个单链表中是否有环: hasCycle
* 9. 判断两个单链表是否相交: isIntersect
* 10. 已知一个单链表中存在环,求进入环中的第一个节点: getFirstNodeInCycle, getFirstNodeInCycleHashMap
* 11. 给出一单链表头指针head和一节点指针delete,O(1)时间复杂度删除节点delete: deleteNode
*/
public class LinkedListSummary {
/**
* @param args
*
*/
public static class Node{
int value;
Node next;
public Node(int n){
this.value=n;
this.next=null;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner in=new Scanner(System.in);
Node head=null;
if(in.hasNextInt()){
head=new Node(in.nextInt());
}
Node temp=head;
while(in.hasNextInt()){
temp.next=new Node(in.nextInt());
temp=temp.next;
}
in.close();
//int len=getListLength(head);
//Node reHead=reverseList(head);
//reHead=reverseListRec(reHead);
//Node node_k=reGetKthNode(head,3);
//Node mid=getMiddleNode(head);
//reversePrintListRec(head);
//reversePrintListStack(head);
//Node mergeHead=mergeSortedList(head,null);
//Node sortHead=listSort(head);
}
//求单链表中结点的个数: getListLength
public static int getListLength(Node head){
int len=0;
while(head!=null){
len++;
head=head.next;
}
return len;
}
//将单链表反转,循环
public static Node reverseList(Node head){
if(head==null||head.next==null)return head;
Node pre=null;
Node nex=null;
while(head!=null){
nex=head.next;
head.next=pre;
pre=head;
head=nex;
}
return pre;
}
//将单链表反转,递归
public static Node reverseListRec(Node head){
if(head==null||head.next==null)return head;
Node reHead=reverseListRec(head.next);
head.next.next=head;
head.next=null;
return reHead;
}
//查找单链表中的倒数第K个结点(k > 0)
public static Node reGetKthNode(Node head,int k){
if(head==null)return head;
int len=getListLength(head);
if(k>len)return null;
Node target=head;
Node nexk=head;
for(int i=0;i<k;i++){
nexk=nexk.next;
}
while(nexk!=null){
target=target.next;
nexk=nexk.next;
}
return target;
}
//查找单链表的中间结点
public static Node getMiddleNode(Node head){
if(head==null||head.next==null)return head;
Node target=head;
Node temp=head;
while(temp!=null&&temp.next!=null){
target=target.next;
temp=temp.next.next;
}
return target;
}
//从尾到头打印单链表,递归
public static void reversePrintListRec(Node head){
if(head==null)return;
else{
reversePrintListRec(head.next);
System.out.println(head.value);
}
}
//从尾到头打印单链表,栈
public static void reversePrintListStack(Node head){
Stack<Node> s=new Stack<Node>();
while(head!=null){
s.push(head);
head=head.next;
}
while(!s.isEmpty()){
System.out.println(s.pop().value);
}
}
//合并两个有序的单链表head1和head2,循环
public static Node mergeSortedList(Node head1,Node head2){
if(head1==null)return head2;
if(head2==null)return head1;
Node target=null;
if(head1.value>head2.value){
target=head2;
head2=head2.next;
}
else{
target=head1;
head1=head1.next;
}
target.next=null;
Node mergeHead=target;
while(head1!=null && head2!=null){
if(head1.value>head2.value){
target.next=head2;
head2=head2.next;
}
else{
target.next=head1;
head1=head1.next;
}
target=target.next;
target.next=null;
}
if(head1==null)target.next=head2;
else target.next=head1;
return mergeHead;
}
//合并两个有序的单链表head1和head2,递归
public static Node mergeSortedListRec(Node head1,Node head2){
if(head1==null)return head2;
if(head2==null)return head1;
if(head1.value>head2.value){
head2.next=mergeSortedListRec(head2.next,head1);
return head2;
}
else{
head1.next=mergeSortedListRec(head1.next,head2);
return head1;
}
}
//对单链表进行排序,归并排序,在排序里面不建议选用递归的合并有序链表算法,如果链表长度较长,很容易出现栈溢出
public static Node listSort(Node head){
Node nex=null;
if(head==null||head.next==null)return head;
else if(head.next.next==null){
nex=head.next;
head.next=null;
}
else{
Node mid=getMiddleNode(head);
nex=mid.next;
mid.next=null;
}
return mergeSortedList(listSort(head),listSort(nex));//合并两个有序链表,不建议递归
}
//对单链表进行排序,插入排序
public Node insertionSortList(Node head) {
if(head==null||head.next==null)return head;
Node pnex=head.next;
Node pnex_nex=null;
head.next=null;
while(pnex!=null){
pnex_nex=pnex.next;
Node temp=head;
Node temp_pre=null;
while(temp!=null){
if(temp.value>pnex.value)break;
temp_pre=temp;
temp=temp.next;
}
if(temp_pre==null){
head=pnex;
pnex.next=temp;
}
else{
temp_pre.next=pnex;
pnex.next=temp;
}
pnex=pnex_nex;
}
return head;
}
//判断一个单链表中是否有环,快慢指针
public static boolean hasCycle(Node head){
boolean flag=false;
Node p1=head;
Node p2=head;
while(p1!=null&&p2!=null){
p1=p1.next;
p2=p2.next.next;
if(p2==p1){
flag=true;
break;
}
}
return flag;
}
//判断两个单链表是否相交,如果相交返回第一个节点,否则返回null
//如果单纯的判断是否相交,只需要看最后一个指针是否相等
public static Node isIntersect(Node head1,Node head2){
Node target=null;
if(head1==null||head2==null)return target;
int len1=getListLength(head1);
int len2=getListLength(head2);
if(len1>=len2){
for(int i=0;i<len1-len2;i++){
head1=head1.next;
}
}else{
for(int i=0;i<len2-len1;i++){
head2=head2.next;
}
}
while(head1!=null&&head2!=null){
if(head1==head2){
target=head1;
break;
}
else{
head1=head1.next;
head2=head2.next;
}
}
return target;
}
//已知一个单链表中存在环,求进入环中的第一个节点,利用hashmap,不要用ArrayList,因为判断ArrayList是否包含某个元素的效率不高
public static Node getFirstNodeInCycleHashMap(Node head){
Node target=null;
HashMap<Node,Boolean> map=new HashMap<Node,Boolean>();
while(head!=null){
if(map.containsKey(head))target=head;
else{
map.put(head, true);
}
head=head.next;
}
return target;
}
//已知一个单链表中存在环,求进入环中的第一个节点,不用hashmap
//用快慢指针,与判断一个单链表中是否有环一样,找到快慢指针第一次相交的节点,此时这个节点距离环开始节点的长度和链表投距离环开始的节点的长度相等
public static Node getFirstNodeInCycle(Node head){
Node fast=head;
Node slow=head;
while(fast!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
if(slow==fast)break;
}
if(fast==null||fast.next==null)return null;//判断是否包含环
//相遇节点距离环开始节点的长度和链表投距离环开始的节点的长度相等
slow=head;
while(slow!=fast){
slow=slow.next;
fast=fast.next;
}//同步走
return slow;
}
//给出一单链表头指针head和一节点指针delete,O(1)时间复杂度删除节点delete
//可惜采用将delete节点value值与它下个节点的值互换的方法,但是如果delete是最后一个节点,则不行,但是总得复杂度还是O(1)
public static void deleteNode(Node head,Node delete){
//首先处理delete节点为最后一个节点的情况
if(delete==null)return;
if(delete.next==null){
if(head==delete)head=null;
else{
Node temp=head;
while(temp.next!=delete){
temp=temp.next;
}
temp.next=null;
}
}
else{
delete.value=delete.next.value;
delete.next=delete.next.next;
}
return;
}
}
双向链表的各种操作(Java实现)
public class LinkList {
Node head;
Node tail;
int count;
public LinkList(Node head,Node tail,int count){
this.head=null;
this.tail=null;
this.count=0;
}
public LinkList(){
}
//尾插法添加节点
public void addHeadNode(NodeData data){
Node node = new Node(data,null,null);
if(head==null&&tail==null){
head=node;
head.setFront(null);
tail=node;
tail.setNext(null);
}else{
head.setFront(node);
node.setNext(head);
head=node;
head.setFront(null);
}
count++;
}
//头插法添加节点
public void addTailNode(NodeData data){
Node node = new Node(data,null,null);
if(head==null&&tail==null){
head=node;
head.setFront(null);
tail=node;
tail.setNext(null);
}else{
tail.setNext(node);
node.setFront(tail);
tail=node;
tail.setNext(null);
}
count++;
}
//查找节点
public Node findNode(NodeData data){
Node temp=head;
if(head!=null){
while(temp!=null){
if(temp.data.compare(data)){
return temp;
}
temp=temp.getNext();
}
}
return null;
}
//删除节点
public void delNode(NodeData data){
Node temp=findNode(data);
if(temp!=null){
if(temp.getFront()==null){
head=temp.getNext();
head.setFront(null);
}else if(temp.getNext()==null){
tail=tail.getFront();
tail.setNext(null);
}else{
temp.getFront().setNext(temp.getNext());
temp.getNext().setFront(temp.getFront());
}
count--;
}
}
//修改更新
public void updNode(NodeData data){
Node temp=findNode(data);
if(temp!=null){
temp.setNodeData(data);
}
}
//打印链表
public void printNode(){
Node temp = head;
while(temp!=null){
temp.print();
temp=temp.getNext();
}
}
}
循环链表的各种操作(Java实现)
public class LinkedListNode {//链表节点
public LinkedListNode previous;//前一节点
public LinkedListNode next;//后一节点
public Object object;//节点中存的值
public long timestamp;
public LinkedListNode(Object object, LinkedListNode next, LinkedListNode previous){
this.object = object;
this.next = next;
this.previous = previous;
}
public void remove() {//删除自己
previous.next = next;
next.previous = previous;
}
public String toString() {
return object.toString();
}
}
public class LinkedList {//循环链表
//头指针,哑的,第一个节点的前面,最后一个节点的后面
private LinkedListNode head = new LinkedListNode("head", null, null);
public LinkedList() {
head.next = head.previous = head;
}
public LinkedListNode getFirst() {//获取循环链表的第一个节点
LinkedListNode node = head.next;
if (node == head) {
return null;
}
return node;
}
public LinkedListNode getLast() {//获取循环链表的最后一个节点
LinkedListNode node = head.previous;
if (node == head) {
return null;
}
return node;
}
public LinkedListNode addFirst(LinkedListNode node) {//将节点插入到链表的第一个位置,头节点之后.
node.next = head.next;
node.previous = head;
node.previous.next = node;
node.next.previous = node;
return node;
}
public LinkedListNode addFirst(Object object) {//将值插入到链表的第一个位置,头节点之后.
LinkedListNode node = new LinkedListNode(object, head.next, head);
node.previous.next = node;
node.next.previous = node;
return node;
}
public LinkedListNode addLast(Object object) {//将值插入到链表的最后一个位置,头节点之前
LinkedListNode node = new LinkedListNode(object, head, head.previous);
node.previous.next = node;
node.next.previous = node;
return node;
}
public void clear() {//清空循环链表
//Remove all references in the list.
LinkedListNode node = getLast();
while (node != null) {
node.remove();
node = getLast();
}
//Re-initialize.
head.next = head.previous = head;
}
public String toString() {
LinkedListNode node = head.next;
StringBuffer buf = new StringBuffer();
while (node != head) {
buf.append(node.toString()).append(",");
node = node.next;
}
return buf.toString();
}
}