zoukankan      html  css  js  c++  java
  • [转]线性表的链式存储

    (java版之单向线性)

    第一步,定义一个接口

    Java代码  收藏代码
    1. package com.stucture.list;  
    2.   
    3. /** 
    4.  * 线性表顺序存储结构的接口 
    5.  * 指的是用一段地址连续的存储单元一次存储线性表的数据元素 
    6.  * @ClassName: ISqList  
    7.  * @author 小学徒 
    8.  * @date 2013-2-27 
    9.  */  
    10. public interface IList<T> {  
    11.       
    12.     /** 
    13.      * 获得元素 
    14.      * @param loc 需要获得的第loc个元素 
    15.      * @return  
    16.      */  
    17.     public T getElem(int loc);  
    18.       
    19.   
    20.     /** 
    21.      * 插入元素 
    22.      * @param loc 元素的插入位置 
    23.      * @param t 需要插入的元素 
    24.      * @return  是否成功插入 
    25.      */  
    26.     public boolean insertElem(int loc, T t);  
    27.       
    28.     /** 
    29.      * 删除元素 
    30.      * @param i 需要删除元素的位置 
    31.      * @return 
    32.      */  
    33.     public T deleteElem(int i);  
    34. }  

    第二步,定义我们的节点类:

    Java代码  收藏代码
    1. package com.stucture.list.linkList;  
    2.   
    3. /** 
    4.  * 链表中的结点 
    5.  * @ClassName: Node  
    6.  * @author 小学徒 
    7.  * @date 2013-2-27 
    8.  */  
    9. public class Node<T> {  
    10.     private T data;       //需要存储的数据信息  
    11.     private Node<T> next; //后继<span style="font-size: 1em; line-height: 1.5;">结</span><span style="font-size: 1em; line-height: 1.5;">点 </span>  
    12.       
    13.     public T getData() {  
    14.         return data;  
    15.     }  
    16.     public void setData(T data) {  
    17.         this.data = data;  
    18.     }  
    19.     public Node<T> getNext() {  
    20.         return next;  
    21.     }  
    22.     public void setNext(Node<T> next) {  
    23.         this.next = next;  
    24.     }  
    25.       
    26.       
    27. }  

    第三步,定义我们的链表及其基本操作,代码如下:

    Java代码  收藏代码
    1. package com.stucture.list.linkList;  
    2.   
    3. import com.stucture.list.IList;  
    4.   
    5. /** 
    6.  * 单链表 
    7.  * @ClassName: LinkList  
    8.  * @author 小学徒 
    9.  * @date 2013-2-27 
    10.  */  
    11. public class LinkList<T> implements IList<T>{  
    12.     private Node<T> head; //链表的结点  
    13.     private int length; //链表的长度  
    14.       
    15.     public LinkList(Node<T> head) {  
    16.         this.head = head;  
    17.     }  
    18.     //获取元素  
    19.     public T getElem(int loc) {  
    20.         int j = 1;  //计数器  
    21.         Node<T> n = head; //指向第一个结点  
    22.           
    23.         while(n != null) {  //n不为空时,循环继续寻找第loc个结点  
    24.             if(j == loc) {  //找到第一个元素时返回  
    25.                 return n.getData();  
    26.             }  
    27.             n = n.getNext();  
    28.             j++;  
    29.               
    30.         }  
    31.         return null;  
    32.     }  
    33.   
    34.     //插入元素  
    35.     public boolean insertElem(int loc, T t) {  
    36.         if(length + 1 < loc) {  
    37.             System.out.println("非法插入");  
    38.             return false;  
    39.         }  
    40.         if(head == null && loc == 1) {  //当第一次插入的时候  
    41.             head = new Node<T>(); //第一次使用,必须创建对象  
    42.             head.setData(t);  
    43.             length++;  
    44.         } else if(head != null && loc == 1) {   //但不是第一次插入,但是插入的位置是第一个时  
    45.             Node<T> tempNode = new Node<T>();   //生成一个新的结点  
    46.             tempNode.setData(t);  
    47.             tempNode.setNext(head);   
    48.             head = tempNode;    //把头换成新插入的结点  
    49.             length++;  
    50.         } else {    //当不是第一次插入并且插入的不是第一个时  
    51.             Node<T> n = this.head;  
    52.             int j = 1;  //计数器  
    53.             while(n != null && j < loc - 1) {  
    54.                 n = n.getNext();  
    55.                 j++;  
    56.             }  
    57.             Node<T> tempNode = new Node<T>();   //生成一个新的结点  
    58.             tempNode.setData(t);  
    59.             tempNode.setNext(n.getNext());  //将n的后继结点赋值给新的结点的后继  
    60.             n.setNext(tempNode);  
    61.             length++;  
    62.         }  
    63.         return true;  
    64.     }  
    65.   
    66.     //删除元素  
    67.     public T deleteElem(int loc) {  
    68.         if(head == null || loc > length) {  
    69.             System.out.println("非法删除");  
    70.             return null;  
    71.         }  
    72.         T old;  
    73.         if(head != null && loc == 1) {  
    74.             old = head.getData();  
    75.             head = head.getNext();  
    76.               
    77.         } else {  
    78.             Node<T> n = this.head;  
    79.             int j = 1;  //计数器  
    80.             while(n != null && j < loc - 1) {  
    81.                 n = n.getNext();  
    82.                 j++;  
    83.             }  
    84.             old = n.getNext().getData();  
    85.             n.setNext(n.getNext().getNext());  
    86.         }  
    87.         length--;  
    88.         return old;  
    89.     }  
    90.   
    91.       
    92.     public Node<T> getHead() {  
    93.         return head;  
    94.     }  
    95.   
    96.     public void setHead(Node<T> head) {  
    97.         this.head = head;  
    98.     }  
    99.   
    100.     public int getLength() {  
    101.         return length;  
    102.     }  
    103.   
    104.     public void setLength(int length) {  
    105.         this.length = length;  
    106.     }  
    107.       
    108. }  

    第四步,代码测试

    Java代码  收藏代码
    1. package com.stucture.list.linkList;  
    2.   
    3. import java.util.Random;  
    4.   
    5. public class LinkListTest {  
    6.     final int MAX = 25;  
    7.     Random r = new Random();  
    8.     LinkList<Integer> linkList;  
    9.       
    10.     public LinkListTest() {  
    11.         initSeqList();  
    12.     }  
    13.       
    14.     //创建一个线性表顺序存储结构  
    15.     public void initSeqList() {  
    16.         linkList = new LinkList<Integer>(null);  
    17.         int length = Math.abs(r.nextInt(MAX));  //使用Random随机产生一个25左右的值,使用Math.abs()函数来取绝对值    
    18.         System.out.println("产生的链表长度为 :" + length);  
    19.           
    20.         for (int i = 1; i <= length; i++) {  //为生成的链表赋值,同时也测试了插入值的方法  
    21.             int j =r.nextInt(MAX);  
    22.             System.out.print(j + " ");  
    23.               
    24.             if(!linkList.insertElem(i, j)) {  
    25.                 System.exit(0);   
    26.             }  
    27.         }  
    28.         System.out.println(" 原始链表是 :");  
    29.         display(linkList);  
    30.     }  
    31.       
    32.     //测试删除方法  
    33.     public void deleteElem() {  
    34.         int i = r.nextInt(MAX);  
    35.         System.out.println(" 删除的位置是:" + i);  
    36.         Integer deleteNumber = linkList.deleteElem(i);  
    37.           
    38.         if( deleteNumber == null) {  
    39.             System.exit(0);  
    40.         } else {  
    41.             System.out.println("删除的元素是 : " + deleteNumber);  
    42.             System.out.println("删除元素后链表是 :");  
    43.             display(linkList);  
    44.         }  
    45.     }  
    46.       
    47.     //测试随机插入方法  
    48.     public void insertByRandom() {  
    49.         int i = r.nextInt(MAX);  
    50.         System.out.println(" 随机插入位置是 :" + i);  
    51.         int elem = r.nextInt(MAX);  
    52.         System.out.println("随机插入数据是 :" + elem);  
    53.         linkList.insertElem(i, elem);  
    54.         System.out.println("随机插入数据后链表是 :");  
    55.         display(linkList);  
    56.     }  
    57.       
    58.     //数据展示  
    59.     public  void display(LinkList<Integer> linkList) {  
    60.         Node<Integer> node = linkList.getHead();  
    61.         while(node != null) {  
    62.             System.out.print(node.getData() + " ");  
    63.             node = node.getNext();  
    64.         }  
    65.         System.out.println("链表的长度为 :" + linkList.getLength());  
    66.     }  
    67.       
    68.     //获取元素  
    69.     public void getElem() {  
    70.         int i = r.nextInt(MAX);  
    71.         System.out.println(" 获取位置为 :" + i);  
    72.         System.out.println("获取到的元素为 : " + linkList.getElem(i));  
    73.           
    74.           
    75.     }  
    76.       
    77.     public static void main(String[] args) {  
    78.         LinkListTest s = new LinkListTest();  
    79.         s.insertByRandom();  
    80.         s.deleteElem();  
    81.         s.getElem();  
    82.     }  
    83. }  

     运行结果同样我不一一把各种结果列出啦,读者可以自己运行多几遍来进行学习:

    运行结果代码  收藏代码
    1. 产生的链表长度为 :23  
    2. 19 18 12 12 15 19 16 21 13 16 18 18 17 13 16 17   
    3. 原始链表是 :  
    4. 19 18 12 12 15 19 16 21 13 16 18 18 17 13 16 17 链表的长度为 :23  
    5.   
    6.   
    7. 随机插入位置是 :24  
    8. 随机插入数据是 :0  
    9. 随机插入数据后链表是 :  
    10. 19 18 12 12 15 19 16 21 13 16 18 18 17 13 16 17 0 链表的长度为 :24  
    11.   
    12.   
    13. 删除的位置是:11  
    14. 删除的元素是 : 13  
    15. 删除元素后链表是 :  
    16. 19 18 12 12 15 19 16 21 16 18 18 17 13 16 17 0 链表的长度为 :23  
    17.   
    18. 获取位置为 :12  
    19. 19 18 12 12 15 19 16 21 16 5 获取到的元素为 : 5  

     (java版之单向循环)

    1. 单向循环链表:将单链表尾结点的指针端由空指针改为指向头结点,使整个单链表形成一个环,这种头尾相接的单链表称为单向循环链表。

    2. 单向循环链表和单链表实现的区别:

    1.)添加一个结点到单向循环链表末尾时,必须使其最后一个结点的指针指向表头结点,而不是象单链表那样置为null。

    2.)判断是否到达表尾时,单向循环链表可以判断该结点是否指向头结点,单链表只需要知道是否为null。

    3.java实现单向循环链表:

    // 单向循环链表
    public class CircularLinkedList<E> {
    	private Node<E> tail; // 尾结点
    	private int size; // 链表长度
    
    	public CircularLinkedList() {
    		tail = null;
    		size = 0;
    	}
    	
    	// 在头结点前插入
    	public boolean addBeforeHead(E data){
    		Node<E> newNode = new Node<E>(data);
    		if(isEmpty()){
    			tail = newNode;
    			tail.setNext(newNode); // 尾结点指向头结点
    			newNode.setNext(tail); // 头结点指向尾结点
    		}else{
    			Node<E> head = tail.getNext();
    			tail.setNext(newNode);
    			newNode.setNext(head);
    		}
    		size++;
    		return true;
    	}
    	
    	// 在尾结点后插入
    	public boolean addAfterTail(E data){
    		Node<E> newNode = new Node<E>(data);
    		if(isEmpty()){
    			tail = newNode;
    			tail.setNext(newNode); 
    			newNode.setNext(tail); 
    		}else{
    			Node<E> head = tail.getNext(); // 获取头结点
    			tail.setNext(newNode); // 将原尾结点指向新结点
    			tail = newNode; // 将新节点设置为尾结点
    			newNode.setNext(head); // 将新尾结点指向头结点
    		}
    		size++;
    		return true;
    	}
    	
    	// 在某位置上插入(position为结点位置,不是角标)
    	public boolean insert(int position,E data){
    		if(position >= 1 && (position <= size + 1)){
    			if(isEmpty() || position == 1){ // 在头结点前插入
    				addBeforeHead(data); 
    			}else if(position == size + 1){ // 在尾结点后插入
    				addAfterTail(data);
    			}else{ // 在中间位置插入
    				Node<E> preNode = get(position - 1); // 获取position的前一结点
    				Node<E> originalNode = preNode.getNext(); // 获取未插入结点时position位置对应结点 
    				Node<E> newNode = new Node<E>(data);
    				preNode.setNext(newNode);
    				newNode.setNext(originalNode);
    				size++;
    				return true;
    			}
    		}
    		return false;
    	}
    	
    	// 删除对应位置的结点
    	public E delete(int position){
    		E result = null;
    		if(position >= 1 && position <= size){
    			if(position == 1){ // 删除头结点
    				result = tail.getNext().getData();
    				Node<E> afterHead = tail.getNext().getNext();
    				tail.setNext(afterHead);
    			}else if(position == size){ // 删除尾结点
    				result = tail.getData();
    				Node<E> preTail = get(position - 1);
    				preTail.setNext(tail.getNext());
    				tail = preTail;
    				size--;
    			}else{ // 删除其他结点
    				Node<E> preNode = get(position - 1);
    				Node<E> curNode = preNode.getNext();
    				result = curNode.getData();
    				preNode.setNext(curNode.getNext());
    				size--;
    			}
    		}
    		return result;
    	}
    	
    	// 获取某个位置的结点
    	public Node<E> get(int position){
    		Node<E> targetNode = null;
    		if(!isEmpty() && position >= 1 && position <= size){ 
    			targetNode = tail.getNext(); // 获取头结点
    			for(int i = 1; i < position ; i++){
    				targetNode = targetNode.getNext(); // 循环获取对应位置的结点
    			}
    		}
    		return targetNode;
    	}
    	
    	// 获取链表的长度
    	public int getSize(){
    		return size;
    	}
    	
    	// 判断链表是否为空
    	public boolean isEmpty(){
    		return size == 0;
    	}
    	
    	// 打印链表中的数据
    	public void display(){
    		Node<E> node = tail.getNext();  // 获取头结点
    		System.out.print("单向循环链表: ");
    		for(int i = 0; i < size; i++){
    			System.out.print(" " + node.getData());
    			node = node.getNext();
    		}
    		System.out.println("");
    	}
    }
    // 结点类,包含结点的数据和指向下一个节点的引用
    public class Node<E> {
    	private E data; // 数据域
    	private Node<E> next; // 指针域保存着下一节点的引用
    
    	public Node() {
    	}
    
    	public Node(E data) {
    		this.data = data;
    	}
    
    	public Node(E data, Node<E> next) {
    		this.data = data;
    		this.next = next;
    	}
    
    	public E getData() {
    		return data;
    	}
    
    	public void setData(E data) {
    		this.data = data;
    	}
    
    	public Node<E> getNext() {
    		return next;
    	}
    
    	public void setNext(Node<E> next) {
    		this.next = next;
    	}
    }
    // 测试类
    public class Main {
    	public static void main(String[] args) {
    		CircularLinkedList<Integer> circular = new CircularLinkedList<Integer>();
    		circular.addBeforeHead(3);
    		circular.addBeforeHead(2);
    		circular.addBeforeHead(1);
    		circular.addAfterTail(4);
    		circular.addAfterTail(5);
    		circular.addAfterTail(6);
    		circular.insert(1,0);
    		circular.insert(8,7);
    		circular.insert(5,8);
    		circular.delete(5);
    		circular.display();
    		System.out.println("链表的长度为: " + circular.getSize());
    	}
    }

     (java版之双向)

    1. 双向链表:在单链表的每个结点中,再设置一个指向其前驱结点的指针域,那么在双向链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前驱。

    2. 单链表和双向链表比较:

    单链表:总是要从头到尾找结点,只能正遍历,不能反遍历。

    双向链表: 可以从头找到尾,也可以从尾找到头,即正反遍历都可以,可以有效提高算法的时间性能,但由于每个结点需要记录两份指针,所以在空间占用上略多一点,这就是通过空间来换时间。

    3. Java实现双向链表:

    // 双向链表
    public class DoubleLinkedList<E> {
    	private Node<E> head; // 头结点
    	private int size; // 链表长度
    
    	// 建立一个空链表
    	public DoubleLinkedList() {
    		head = null;
    		size = 0;
    	}
    	
    	// 在头结点前插入
    	public boolean addBeforeHead(E data){
    		Node<E> newNode = new Node<E>(data);
    		if(isEmpty()){
    			head = newNode;
    		}else{
    			head.setPrev(newNode);
    			newNode.setNext(head);
    			head = newNode;
    		}
    		size++;
    		return true;
    	}
    	
    	// 在链表尾部插入
    	public boolean addAfterTail(E data){
    		Node<E> newNode = new Node<E>(data);
    		if(isEmpty()){
    			head = newNode;
    		}else{
    			Node<E> tail = get(size);
    			tail.setNext(newNode);
    			newNode.setPrev(tail); // 设置新结点的上一结点
    		}
    		size++;
    		return true;
    	}
    
    	// 插入指定位置的结点
    	public boolean insert(int position, E data) {
    		if(position >= 1 && (position <= size + 1)){
    			Node<E> newNode = new Node<E>(data);
    			if(isEmpty() || position == 1){ // 链表为空或在头结点前插入
    				addBeforeHead(data);
    			}else if(position == size + 1){ // 在尾结点后插入
    				Node<E> preNode = get(position - 1);
    				newNode.setPrev(preNode);
    				preNode.setNext(newNode);
    			}else{ // 在其他位置插入
    				Node<E> preNode = get(position - 1); // 获取position的前一结点
    				Node<E> afterNode = preNode.getNext(); // 获取未插入结点时position位置对应结点 
    				newNode.setPrev(preNode); // ①
    				newNode.setNext(afterNode); // ②
    				afterNode.setPrev(newNode); // ③
    				preNode.setNext(newNode); // ④
    			}
    			size++;
    			return true;
    		}
    		return false;
    	}
    
    	// 删除指定位置的结点
    	public E delete(int position) { 
    		E result = null;
    		if(position >= 1 && position <= size){
    			if(position == 1){ // 删除头结点
    				result = head.getData();
    				Node<E> afterHead = head.getNext();
    				afterHead.setPrev(null);
    				head.setNext(null);
    				head = afterHead; 
    			}else if(position == size){ // 删除尾结点
    				Node<E> preNode = get(position - 1); // 获取待删除结点的前一结点
    				Node<E> delNode = preNode.getNext(); // 获取待删除结点
    				result = delNode.getData();
    				preNode.setNext(null);
    			}else{ // 删除其他结点
    				Node<E> preNode = get(position - 1); // 获取待删除结点的前一结点
    				Node<E> delNode = preNode.getNext(); // 获取待删除结点
    				result = delNode.getData();
    				Node<E> nextNode = delNode.getNext();// 获取待删除结点的下一结点
    				preNode.setNext(nextNode); // ①
    				nextNode.setPrev(preNode); // ②
    			}
    			size--;
    		}
    		return result;
    	}
    	
    	// 获取某个位置的结点(正序遍历)
    	public Node<E> get(int position){
    		Node<E> targetNode = null;
    		if(!isEmpty() && position >= 1 && position <= size){ 
    			targetNode = head;
    			for(int i = 1; i < position ; i++){
    				targetNode = targetNode.getNext(); // 循环获取对应位置的结点
    			}
    		}
    		return targetNode;
    	}
    	
    	// 获取链表的长度
    	public int getSize(){
    		return size;
    	}
    	
    	// 判断链表是否为空
    	public boolean isEmpty(){
    		return size == 0;
    	}
    	
    	// 打印链表数据
    	public void display(){
    		Node<E> node = head;
    		System.out.print("双向链表: ");
    		for(int i = 0; i < size; i++){
    			System.out.print(" " + node.getData());
    			node = node.getNext();
    		}
    		System.out.println("");
    	}
    }
    //结点类,包含结点的数据和指向下一个节点的引用
    public class Node<E> {
    	private E data; // 数据域
    	private Node<E> next; // 指针域保存着下一节点的引用
    	private Node<E> prev; // 指针域保存着上一节点的引用 (相比单链表,双向链表多了这个指针)
    	
    	public Node() {
    	}
    
    	public Node(E data) {
    		this.data = data;
    	}
    
    	public Node(E data, Node<E> next, Node<E> prev) {
    		this.data = data;
    		this.next = next;
    		this.prev = prev;
    	}
    
    	public E getData() {
    		return data;
    	}
    
    	public void setData(E data) {
    		this.data = data;
    	}
    
    	public Node<E> getNext() {
    		return next;
    	}
    
    	public void setNext(Node<E> next) {
    		this.next = next;
    	}
    
    	public Node<E> getPrev() {
    		return prev;
    	}
    
    	public void setPrev(Node<E> prev) {
    		this.prev = prev;
    	}
    }
    public class Main {
    	public static void main(String[] args) {
    		DoubleLinkedList<Integer> dll = new DoubleLinkedList<Integer>();
    		dll.addBeforeHead(2);
    		dll.addAfterTail(3);
    		dll.addBeforeHead(1);
    		dll.display();
    		dll.insert(4,4);
    		dll.insert(5,5);
    		dll.insert(6,6);
    		dll.display();
    		dll.delete(6);
    		dll.delete(3);
    		dll.delete(1);
    		dll.display();
    		System.out.println("双向链表的长度为:  " + dll.getSize());
    	}
    }


    (java版之静态单向)

    1. 静态链表:用数组描述的链表叫静态链表,通常为方便数据插入,我们会把数组建的大一些。

    2. 数组元素(node):由两个数据域组成(data,cursor)。数据域data用来存放数据元素,也就是通常我们要处理的数据;而cursor相当于单链表中的指针,存放该元素的后继在数组中的下标

    3. java实现静态链表:

    // 静态链表
    class StaticLinkedList {
    	private int size;
    	private Node[] node = new Node[100];
    
    	// 用数组初始化静态链表
    	public StaticLinkedList(int arr[]) {
    		for (int i = 0; i < 100; i++) {
    			node[i] = new Node(); // 初始化100个结点对象(感觉性能不会很好)
    		}
    		for (int j = 0; j < arr.length; j++) {
    			node[j + 1].setData(arr[j]);  // 第一个结点为头结点,头结点没有数据,只存索引
    			node[j].setCursor(j + 1);
    		}
    		size = arr.length;
    	}
    
    	// 在某位置插入值
    	public void insert(int index, int value) {
    		validateIndex(index);
    		int curIndex = node[index].getCursor();    // 获取待插入结点的前一个结点指针,它记住的是原待插入结点位置
    		node[size + 1].setData(value); // 第一个结点为头结点,所以新插入的结点为node[size + 1]
    		node[size + 1].setCursor(curIndex); // 让新插入的结点记住原位置结点角标
    		node[index].setCursor(size + 1); // 让原位置前一结点记住新插入结点角标
    		size++;
    	}
    
    	// 删除指定位置的值
    	public void delete(int index) { 
    		validateIndex(index);
    		int curIndex = node[index].getCursor(); // 获取待删除节点的前一个结点指针,它记住的是待删除结点角标(注:第一个结点为头结点)
    		int nextIndex = node[curIndex].getCursor(); // 获取待删除节点指针,它记住的是待删除的下一个结点角标
    		node[index].setCursor(nextIndex); // 将待删除结点的前一个结点指针指向待删除结点的下一个结点角标
    		size--;
    	}
    	
    	// 验证下标值是否合法,非法时抛出异常。
    	private void validateIndex(int index) {
    		if (index < 0 || index > size) {
    			throw new IndexOutOfBoundsException("无效的下标:" + index);
    		}
    	}
    
    	// 输出所有元素
    	public void display() {
    		int nextIndex = node[0].getCursor();  // node[0] 为头结点,存的是下一个结点角标
    		int i = 0;
    		while (i < size) {
    			System.out.printf("%d	", node[nextIndex].getData());
    			nextIndex = node[nextIndex].getCursor();
    			i++;
    		}
    	}
    }
    // 结点(数组元素)
    class Node {
    	int data; // 记录存入的数据 
    	int cursor; // 记录下一个数据的下标
    	public int getData() {
    		return data;
    	}
    	public void setData(int data) {
    		this.data = data;
    	}
    	public int getCursor() {
    		return cursor;
    	}
    	public void setCursor(int cursor) {
    		this.cursor = cursor;
    	}
    }
    // 测试类
    public class Main {
    	public static void main(String[] args) {
    		int arr[] = { 1, 3, 4, 5, 6 };
    		StaticLinkedList list = new StaticLinkedList(arr);
    		System.out.print("初始化:
    ");
    		list.display();
    		System.out.print("
    在角标为1的位置插入2后:
    ");
    		list.insert(1, 2);
    		list.display();
    		System.out.print("
    删除角标为5的结点后:
    ");
    		list.delete(5);
    		list.display();
    	}
    }
    4. 静态链表的优越点:

    优点:在插入和删除操作时,不需要移动元素,从而改进了在顺序存储结构中插入和删除操作需要移动大量元素的缺点。

    缺点:没有解决连续存储分配带来表长难以确定的问题。

    5. 总的来说,静态链表其实是为了给没有指针的高级语言设计的一种实现单链表的方法,一般很少用。

    (C版之静态单链表之一)

    // c1.h----线性表的静态单链表存储结构------

    typedef int ElemType;
    #define MAXSIZE 100 // 链表的最大长度

    //  线性表的静态单链表存储结构
    typedef struct{
     ElemType data; //存储数据,数据域
     int cur;  //游标指示器cur,代替指针指示结点在数组中的位置
    }component,SLinkList[MAXSIZE];

    // c2.h -----线性表的静态单链表的基本操作------

    // 先将L的最后一个单元L[MAXSIZE-1]为表头,构造一个空的链表,然后将其余单元链成一个备用链表,这个

    //备用链表的表头为L的第一个单元L[0],无数据域。“0”表示空指针,初始化各元素的游标指示器cur
    void InitList(SLinkList L){
     int i;
     // L的最后一个单元为空链表的表头,无数据域
     L[MAXSIZE-1].cur=0; 
     // 将其余单元链接成以L[0]为表头的备用链表
     for(i=0;i<MAXSIZE-2;i++)
      L[i].cur=i+1;
     //这个为什么也置为零呢?因为它是那个一L[0]为表头的备用链表的尾结点
     L[MAXSIZE-2].cur=0; 
    }


    // 将L重置为空表
    int ClearList(SLinkList L){
     int i,j,k;
     i=L[MAXSIZE-1].cur; // 链表第一个结点的位置
     L[MAXSIZE-1].cur=0; // 链表置空空
     k=L[0].cur;   // 备用链表第一个结点的位置
     L[0].cur=i;   // 把链表的结点连到备用链表的表头
     while(i) // 没到链表尾,继续循环
     {
      j=i;
      i=L[i].cur; // 指向下一个元素
     }
     L[j].cur=k; // 备用链表的第一个结点接到链表的尾部
     return 1;
    }


    // 若L是空表(第一个结点在数组的第0个位置,则是空表),返回1;否则返回0
    int ListEmpty(SLinkList L){
     if(L[MAXSIZE-1].cur==0) // 空表
      return 1;
     else
      return 0;
    }

    // 返回L中数据元素个数
    int ListLength(SLinkList L){
     int j=0, //用来统计结点的个数
      i=L[MAXSIZE-1].cur; // i指向第一个元素
     while(i) // 没到静态链表尾,继续循环
     {
      i=L[i].cur; // 指向下一个元素
      j++;
     }
     return j;
    }

    // 用e返回L中第i个元素的值
    int GetElem(SLinkList L,int i,ElemType *e){
     int l,
      k=MAXSIZE-1; // k指向表头序号
     if(i<1||i>ListLength(L))
      return 0;
     for(l=1;l<=i;l++) // 移动到第i个元素处
      k=L[k].cur;
     *e=L[k].data;
     return 1;
    }


    // 算法2.13  P32
    // 在静态单链线性表L中查找第1个值为e的元素。若找到,则返回它在L中的
    // 位序,否则返回0。(与其它LocateElem()的定义不同)
    int LocateElem(SLinkList L,ElemType e) { 
     int i=L[MAXSIZE-1].cur; // i指示表中第一个结点
     while(i&&L[i].data!=e) // 在表中顺链查找(e不能是字符串)
      i=L[i].cur;
     return i;
    }

    // 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱
    int PriorElem(SLinkList L,ElemType cur_e,ElemType *pre_e){ 
     int j, //用来存储i所对应元素的前驱结点的位置
     i=L[MAXSIZE-1].cur; // i指示链表第一个结点的位置
     do
     { // 向后移动结点
      j=i;
      i=L[i].cur;
     }while(i&&cur_e!=L[i].data);
     if(i) // 找到该元素
     {
      *pre_e=L[j].data;
      return 1;
     }
     return 0;
    }

    // 若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继
    int NextElem(SLinkList L,ElemType cur_e,ElemType *next_e){ 
     int j,
      i=LocateElem(L,cur_e); // 在L中查找第一个值为cur_e的元素的位置
     if(i) // L中存在元素cur_e
     {
      j=L[i].cur; // cur_e的后继的位置
      if(j) // cur_e有后继
      {
       *next_e=L[j].data;
       return 1; // cur_e元素有后继
      }
     }
     return 0; // L不存在cur_e元素,cur_e元素无后继
    }

    // 算法2.15
    // 若备用链表非空,则返回分配的结点下标(备用链表的第一个结点),否则返回0
    int Malloc(SLinkList space)
    {
     int i=space[0].cur;
     if(i) // 备用链表非空
      // 备用链表的头结点指向原备用链表的第二个结点,因为第一个结点
      // 已经被覆盖了
      space[0].cur=space[i].cur;
     return i; // 返回新开辟结点的坐标
    }

    // 算法2.16
    // 将下标为k的空闲结点回收到备用链表(成为备用链表的第一个结点)
    void Free(SLinkList space,int k){
     // 回收结点的"游标"指向备用链表的第一个结点
     space[k].cur=space[0].cur;
     space[0].cur=k; // 备用链表的头结点指向新回收的结点
    }

    void DestroyList(){
     // 静态数组不能被销毁
    }

    // 在L中第i个元素之前插入新的数据元素e
    int ListInsert(SLinkList L,int i,ElemType e){
     int l,j,k=MAXSIZE-1; // k指向表头
     
     if(i<1||i>ListLength(L)+1)
      return 0;
     j=Malloc(L); // 申请新单元
     if(j) // 申请成功
     {
      L[j].data=e; // 赋值给新单元
      for(l=1;l<i;l++) // 移动i-1个元素
       k=L[k].cur;
      L[j].cur=L[k].cur;
      L[k].cur=j;
      return 1;
     }
     return 0;
    }

    // 删除在L中第i个数据元素e,并返回其值
    int ListDelete(SLinkList L,int i,ElemType *e){
     int j,k=MAXSIZE-1; // k指向表头
     if(i<1||i>ListLength(L))
      return 0;
     for(j=1;j<i;j++) // 移动i-1个元素
      k=L[k].cur;
     j=L[k].cur;
     L[k].cur=L[j].cur;
     *e=L[j].data;
     Free(L,j);
     return 1;
    }


    // 依次对L的每个数据元素调用函数vi()
    int ListTraverse(SLinkList L,void(*vi)(ElemType)){  
     int i=L[MAXSIZE-1].cur; // 指向第一个元素
     
     while(i) // 没到静态链表尾
     {
      vi(L[i].data); // 调用vi()
      i=L[i].cur; // 指向下一个元素
     }
     printf(" ");
     return 1;
    }


    void visit(ElemType c)
    {
     printf("%d ",c);
    }

    //main (检验基本操作的主程序)

    #include <stdio.h>
    #include <stdlib.h>
    #include"c1.h"
    #include"c2.h"

    int main()
    {
     int j,k;
     int i;
     ElemType e,e0;
     SLinkList L;
     InitList(L);
     for(j=1;j<=5;j++)
      i=ListInsert(L,1,j);
     printf("在L的表头依次插入1~5后:L=");
     ListTraverse(L,visit);
     i=ListEmpty(L);
     printf("L是否空:i=%d(1:是 0:否)表L的长度=%d ",i,ListLength(L));
     i=ClearList(L);
     printf("清空L后:L=");
     ListTraverse(L,visit);
     i=ListEmpty(L);
     printf("L是否空:i=%d(1:是 0:否)表L的长度=%d ",i,ListLength(L));
     for(j=1;j<=10;j++)
      ListInsert(L,j,j);
     printf("在L的表尾依次插入1~10后:L=");
     ListTraverse(L,visit);
     GetElem(L,5,&e);
     printf("第5个元素的值为:%d ",e);
     for(j=0;j<=1;j++)
     {
      k=LocateElem(L,j);
      if(k)
       printf("值为%d的元素在静态链表中的位序为%d ",j,k);
      else
       printf("没有值为%d的元素 ",j);
     }
     for(j=1;j<=2;j++) // 测试头两个数据
     {
      GetElem(L,j,&e0); // 把第j个数据赋给e0
      i=PriorElem(L,e0,&e); // 求e0的前驱
      if(!i)
       printf("元素%d无前驱 ",e0);
      else
       printf("元素%d的前驱为:%d ",e0,e);
     }
     for(j=ListLength(L)-1;j<=ListLength(L);j++) // 最后两个数据
     {
      GetElem(L,j,&e0); // 把第j个数据赋给e0
      i=NextElem(L,e0,&e); // 求e0的后继
      if(!i)
       printf("元素%d无后继 ",e0);
      else
       printf("元素%d的后继为:%d ",e0,e);
     }
     k=ListLength(L); // k为表长
     for(j=k+1;j>=k;j--)
     {
      i=ListDelete(L,j,&e); // 删除第j个数据
      if(i)
       printf("删除的元素为:%d ",e);
      else
       printf("删除第%d个数据失败 ",j);
     }
     printf("依次输出L的元素:");
     ListTraverse(L,visit); // 依次对元素调用visit(),输出元素的值
     
     system("pause");
     return 0;
    }

    线性表的静态单链表存储结构表示和实现

    -------------------------------------------------------------------------------------------------
    输出效果:

    在L的表头依次插入1~5后:L=5 4 3 2 1
    L是否空:i=0(1:是 0:否)表L的长度=5
    清空L后:L=
    L是否空:i=1(1:是 0:否)表L的长度=0
    在L的表尾依次插入1~10后:L=1 2 3 4 5 6 7 8 9 10
    第5个元素的值为:5
    没有值为0的元素
    值为1的元素在静态链表中的位序为5
    元素1无前驱
    元素2的前驱为:1
    元素9的后继为:10
    元素10无后继
    删除第11个数据失败
    删除的元素为:10
    依次输出L的元素:1 2 3 4 5 6 7 8 9
    请按任意键继续. . .

    (C版之单链表存储之二)

    //  c1.h  线性表的单链表存储结构
    typedef struct LNode{
     ElemType data;  //数据域
     struct LNode *next; //指针域
    }LNode, *LinkList;

    //  c2.h  线性表的单链表的基本操作

    // 构造一个空的线性表L
    int InitList(LinkList *L){
      //产生头结点L,并使L指向此头结点,头节点的数据域为空,不放数据的。
      //void * malloc(size_t)
      //这里对返回值进行强制类型转换了,返回值是指向空类型的指针类型。
     (*L) = (LinkList)malloc( sizeof(struct LNode) );
     if( !(*L) )
      exit(0);  // 存储分配失败
     (*L)->next = NULL; // 指针域为空
     return 1;
    }

    // 销毁线性表L,将包括头结点在内的所有元素释放其存储空间。
    int DestroyList(LinkList *L){  
     LinkList q;

     // 由于单链表的每一个元素是单独分配的,所以要一个一个的进行释放
     while( *L ) {
      q = (*L)->next;
      free( *L );  //释放
      *L = q;
     }
     return 1;
    }


    //将L重置为空表,即将链表中除头结点外的所有元素释放其存储空间

    //但是将头结点指针域置空,这和销毁有区别哦。不改变L,所以不需要用指针。
    int ClearList( LinkList L ){  
     LinkList p, q;

     p = L->next; // p指向第一个结点
     while( p )  // 没到表尾则继续循环
     {
      q = p->next;
      free( p ); //释放空间
      p = q;
     }
     L->next = NULL; // 头结点指针域为空,链表成了一个空表 
     return 1;
    }

    // 若L为空表(根据头结点L->next来判断,为空则是空表),则返回1, 否则返回0。
    int ListEmpty(LinkList L){  
     if( L->next ) // 非空
      return 0;
     else
      return 1;
    }

    // 返回L中数据元素个数。
    int ListLength(LinkList L){  
     int i = 0;
     LinkList p = L->next; // p指向第一个结点 
     while(p) // 没到表尾,则继续循环
     {
      i++;
      p=p->next;
     }
     return i;
    }

    // 算法2.8 P29
    // L为带头结点的单链表的头指针。当第i个元素存在时,其值赋给e并返回1,否则返回0。
    int GetElem(LinkList L,int i,ElemType *e){
     int j = 1;   // j为计数器
     LinkList p=L->next; // p指向第一个结点
     while(p&&j<i)  // 顺指针向后查找,直到p指向第i个元素或p为空
     {
      p=p->next;
      j++;
     } 

     if(!p||j>i) // 第i个元素不存在
      return 0;
     *e = p->data; // 取第i个元素
     return 1;
    }

    // 返回L中第1个与e满足关系compare()的数据元素的位序。 若这样的数据元素不存在,则返回值为0
    int LocateElem(LinkList L,ElemType e,int(*compare)(ElemType,ElemType)){
     int i=0;
     LinkList p=L->next;

     while(p) //将链表的每一个元素进行对比
     {
      i++;
      if(compare(p->data,e)) // 找到这样的数据元素
       return i;
      p=p->next;
     }
     return 0;
    }

    // 若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,
    // 返回1;否则操作失败,pre_e无定义,返回-1
    int PriorElem(LinkList L,ElemType cur_e,ElemType *pre_e){
     LinkList q,
        p=L->next; // p指向第一个结点
     while(p->next)  // p所指结点有后继
     {
      q=p->next; // q为p的后继
      if(q->data==cur_e)
      {
       *pre_e=p->data;
       return 1;
      }
      p=q; // p向后移
     }
     return -1;
    }

    // 若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,
    // 返回1;否则操作失败,next_e无定义,返回-1
    int NextElem(LinkList L,ElemType cur_e,ElemType *next_e){
     LinkList p=L->next; // p指向第一个结点
     while(p->next) // p所指结点有后继
     {
      if(p->data==cur_e){
       *next_e=p->next->data;
       return 1;
      }
      p=p->next;
     }
     return -1;
    }

    // 算法2.9 P30
    // 在带头结点的单链线性表L中第i个位置之前插入元素e
    int ListInsert(LinkList *L,int i,ElemType e){
     int j=0;
     LinkList p=*L,s;
     while(p && j<i-1) // 寻找第i-1个结点
     {
      p=p->next;
      j++;
     }
     if(!p || j>i-1) // i小于1或者大于表长
      return 0;
     s=(LinkList)malloc(sizeof(struct LNode)); // 生成新结点
     s->data=e; // 插入L中
     s->next=p->next;
     p->next=s;
     return 1;
    }

    // 算法2.10 P30
    // 在带头结点的单链线性表L中,删除第i个元素,并由e返回其值
    int ListDelete(LinkList *L, int i,ElemType *e){
     int j = 0;
     LinkList p=*L,q;
     while(p->next&&j<i-1) // 寻找第i个结点,并令p指向其前趋
     {
      p=p->next;
      j++;
     }
     if(!p->next||j>i-1) // 删除位置不合理
      return 0;
     q=p->next; // 删除并释放结点
     p->next=q->next;
     *e=q->data;
     free(q);
     return 1;
    }

    // 依次对L的每个数据元素调用函数vi()
    int ListTraverse(LinkList L,void(*vi)(ElemType)){
     LinkList p=L->next;
     //对所有元素调用函数vi
     while(p)
     {
      vi(p->data);
      p=p->next;
     }
     printf(" ");

     return 1;
    }

    // 在按非降序排列的线性表L中按非降序插入新的数据元素e
    void InsertAscend(LinkList L,ElemType e){
     LinkList q=L,
        p=L->next;

     while(p&&e>p->data)
     {
      q=p;
      p=p->next;
     }
     q->next=(LinkList)malloc(sizeof(struct LNode)); // e插在q后
     q->next->data=e;
     q->next->next=p;
    }

    // 按非升序排列的线性表L中按非升序插入新的数据元素e
    void InsertDescend(LinkList L,ElemType e){
     LinkList q=L,p=L->next;
     while(p&&e<p->data)
     {
      q=p;
      p=p->next;
     }
     q->next=(LinkList)malloc(sizeof(struct LNode)); // e插在q后
     q->next->data=e;
     q->next->next=p;
    }

    // L的头部插入新的数据元素e,作为链表的第一个元素
    int HeadInsert(LinkList L,ElemType e){
     LinkList s;
     s=(LinkList)malloc(sizeof(struct LNode)); // 生成新结点
     s->data=e; // 给结点赋值
     s->next=L->next; // 插在表头
     L->next=s;
     return 1;
    }

    // 在L的尾部插入新的数据元素e,作为链表的最后一个元素
    int EndInsert(LinkList L,ElemType e){
     LinkList p=L;
     while(p->next) // 使p指向表尾元素
      p=p->next;
     p->next=(LinkList)malloc(sizeof(struct LNode)); // 在表尾生成新结点
     p->next->data=e; // 给新结点赋值
     p->next->next=NULL; // 表尾
     return 1;
    }

    // 删除L的第一个数据元素,并由e返回其值
    int DeleteFirst(LinkList L,ElemType *e){
     LinkList p=L->next;
     if(p)
     {
      *e=p->data;
      L->next=p->next;
      free(p);
      return 1;
     }
     else
      return 0;
    }

    // 删除L的最后一个数据元素,并用e返回其值
    int DeleteTail(LinkList L,ElemType *e)
    {
     LinkList p=L,q;
     if(!p->next) // 链表为空
      return 0;
     while(p->next)
     {
      q=p;
      p=p->next;
     }
     q->next=NULL; // 新尾结点的next域设为NULL
     *e=p->data;
     free(p);
     return 1;
    }

    // 删除表中值为e的元素,并返回1;如无此元素,则返回0
    int DeleteElem(LinkList L,ElemType e){
     LinkList p=L,q;
     while(p)
     {
      q=p->next;
      if(q&&q->data==e)
      {
       p->next=q->next;
       free(q);
       return 1;
      }
      p=q;
     }
     return 0;
    }

    // 用e取代表L中第i个元素的值
    int ReplaceElem(LinkList L,int i,ElemType e){
     LinkList p=L;
     int j=0;
     //找到第i个元素的位置给p
     while(p->next && j<i)
     {
      j++;
      p=p->next;
     }
     if(j==i)
     {
      p->data=e;
      return 1;
     }
     else // 表中不存在第i个元素
      return 0;
    }

    // 按非降序建立n个元素的线性表
    int CreatAscend(LinkList *L,int n){
     int j;
     LinkList p,q,s;
     if(n<=0)
      return 0;
     InitList(L);
     printf("请输入%d个元素:(空格) ",n);
     s=(LinkList)malloc(sizeof(struct LNode)); // 第一个结点
     scanf("%d",&s->data);
     s->next=NULL;
     (*L)->next=s;
     for(j=1;j<n;j++)
     {
      s=(LinkList)malloc(sizeof(struct LNode)); // 其余结点
      scanf("%d",&s->data);
      q=*L;
      p=(*L)->next;
      while(p&&p->data<s->data) // p没到表尾,且所指元素值小于新值
      {
       q=p;
       p=p->next; // 指针后移
      }
      s->next=q->next; // 元素插在q的后面
      q->next=s;
     }
     return 1;
    }

    // 按非升序建立n个元素的线性表
    int CreatDescend(LinkList *L,int n)
    {
     int j;
     LinkList p,q,s;
     if(n<=0)
      return 0;
     InitList(L);
     printf("请输入%d个元素:(空格) ",n);
     s=(LinkList)malloc(sizeof(struct LNode)); // 第一个结点
     scanf("%d",&s->data);
     s->next=NULL;
     (*L)->next=s;
     for(j=1;j<n;j++)
     {
      s=(LinkList)malloc(sizeof(struct LNode)); // 其余结点
      scanf("%d",&s->data);
      q=*L;
      p=(*L)->next;
      while(p&&p->data>s->data) // p没到表尾,且所指元素值大于新值
      {
       q=p;
       p=p->next; // 指针后移
      }
      s->next=q->next; // 元素插在q的后面
      q->next=s;
     }
     return 1;
    }

    // 返回表头元素的值
    int GetFirstElem(LinkList L,ElemType *e)
    {
     LinkList p=L->next; //第一个结点给p
     if(!p) // 空表
      return 0;
     else // 非空表
      *e=p->data;
     return 1;
    }


    // 算法2.11 P30
    // 逆位序(插在表头)输入n个元素的值,建立带表头结构的单链线性表L
    void CreateList(LinkList *L,int n){
     int i;
     LinkList p;
     // 先建立一个带头结点的空单链表,相当于初始化单链表
     *L=(LinkList)malloc(sizeof(struct LNode));
     (*L)->next=NULL; 
      printf("请输入%d个数据 ",n);
     for(i=n;i>0;--i)
     {
      p=(LinkList)malloc(sizeof(struct LNode)); // 生成新结点
      scanf("%d",&p->data); // 输入元素值
      p->next=(*L)->next; // 插入到表头
      (*L)->next=p;
     }
    }

    // 正位序(插在表尾)输入n个元素的值,建立带表头结构的单链线性表
    void CreateList2(LinkList *L,int n){
     int i;
     LinkList p,q;
     // 先建立一个带头结点的空单链表,相当于初始化单链表
     *L=(LinkList)malloc(sizeof(struct LNode)); // 生成头结点
     (*L)->next=NULL;
     q=*L;
     printf("请输入%d个数据 ",n);
     for(i=1;i<=n;i++)
     {
      p=(LinkList)malloc(sizeof(struct LNode));
      scanf("%d",&p->data);
      q->next=p;
      q=q->next;
     }
     p->next=NULL;
    }



    -------------------------------------------------------------------------------------------- 
     用单链表重写 算法2.2 供参考
     已知线性表La和Lb中的数据元素按值非递减排列。
     归并La和Lb得到新的线性表Lc,Lc的数据元素也按值非递减排列

    void MergeList(LinkList La,LinkList Lb,LinkList *Lc)
    {
     int i=1,j=1,k=0;
     int La_len,Lb_len;
     ElemType ai,bj;
     InitList(Lc);
     La_len=ListLength(La);
     Lb_len=ListLength(Lb);
     while(i<=La_len&&j<=Lb_len) // 表La和表Lb均非空
     {
      GetElem(La,i,&ai);
      GetElem(Lb,j,&bj);
      if(ai<=bj)
      {
       ListInsert(Lc,++k,ai);
       ++i;
      }
      else
      {
       ListInsert(Lc,++k,bj);
       ++j;
      }
     }
     while(i<=La_len) // 表La非空且表Lb空
     {
      GetElem(La,i++,&ai);
      ListInsert(Lc,++k,ai);
     }
     while(j<=Lb_len) // 表Lb非空且表La空
     {
      GetElem(Lb,j++,&bj);
      ListInsert(Lc,++k,bj);
     }
    }
    ----------------------------------------------------------------------------------------------

    // 算法2.12 P31
    // 已知单链线性表La和Lb的元素按值非递减排列。
    // 归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列
    void MergeList(LinkList La,LinkList *Lb,LinkList *Lc) {
     LinkList pa=La->next,pb=(*Lb)->next,pc;
     *Lc=pc=La; // 用La的头结点作为Lc的头结点
     while(pa&&pb){
      if(pa->data <= pb->data){
       pc->next=pa;
       *Lc=pa;
       pa=pa->next;
      }
      else{
       pc->next=pb;
       pc=pb;
       pb=pb->next;
      }
     }
     pc->next=pa ? pa : pb; // 插入剩余段
     free(*Lb); // 释放Lb的头结点
     Lb=NULL;
    }


    // 判断是否相等的函数,Union()用到
    int equal(ElemType c1,ElemType c2)
    {
     if(c1==c2)
      return 1;
     else
      return 0;
    }

    // 算法2.1
    // 将所有在线性表Lb中但不在La中的数据元素插入到La中
    void Union(LinkList La,LinkList Lb){
     ElemType e;
     int La_len,Lb_len;
     int i;
     La_len=ListLength(La); // 求线性表的长度
    &nbs