线性表——单链表(java)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40301026/article/details/86768703

       一、单链表是一种链式存取的数据结构。是由若干个结点组成的,每个结点由:元素(数据元素的映象) + 指针(指示后继元素存储位置)两部分组成。需要注意的是结点的地址是随机给定的。

在逻辑上可以理解为这样的: 

                 

上图是不带头结点的单链表

        单链表的一个重要特征就是能从前驱结点找到后继结点。而无法从后继结点找前驱结点。因为这种最普通的单链表只有一个next引用(我习惯叫指针)指向后继结点。

     二、当然你也可以带头结点的那种单链表,只是将上图的第一个结点当做头结点,一般什么都不装入或者是装入一些链表信息例如:链表长度等等。

                      

    三、我写的是不带头结点的,其实区别也不是太大。主要是能理解其主要的逻辑思维就行。

 其结点的结构为:

package cn.liu.Link_list;

//定义一个单链表结点
public class Node {
	public Object data;  //数据域
	public Node next;    //next引用,相当于指针。注意它的类型为Node

    //为了方便,建了一个构造器不影响,可以没有。
	public Node(Object data) {
		super();
		this.data = data;
		this.next = null;
	}
}

接下来是链表的一些操作:

1.建立一个单链表

这里有头插法和尾插法两种方法去建立一个链表,初始化一个空白链表。

头插法是往第一个结点的前面的插,形成一个链表。

public class Link_list {
	
	 Node head = null;//头指针永远指向首地址
	 public int size = 0;//记录结点的个数
     //增加了一个size是为了掌控单链表的结点个数,没有什么影响。
}
​
//头插法建立一个单链表
	public void initlist_head(int i) {
		detection1(i);//这是对索引i的合格的检测,后面会贴整体代码。
		for(int j = 0; j<i ; j++) {
			Node newnode = new Node(null);//新建一个新的结点
			if(j==0) {
				head = newnode; //没有建立时,newnode便是第一个结点
			}else {
				newnode.next = head;//插在头结点的前面
				head = newnode;//头指针始终在首结点
			}
		}
		size = i;
	}

​

尾插法是每次往最后一个结点加入,最后形成一个链表。

//尾插法建立一个单链表
	public void initlist_tail(int i) {
		detection1(1);//对索引i的合格检测
		Node tail = head;//永远指向最后一个结点
		
		for(int j = 0; j < i; j++) {
			Node newnode = new Node(null);
			if(j==0) {
				head = newnode;
			}else {
				tail.next = newnode;
			}
			tail = newnode;
		}
		size = i;
	}

   2.插入到链表的指定位置

//插入到指定位置
	public void addsomewhere(Object e,int i) {
		detection2(i);//索引是否合格检测
		Node newnode = new Node(e);
		Node temp = head;//相当于遍历指针
		Node p = temp;//指向temp后面的结点
		
		if(i==1) {
			newnode.next = head;//插在头结点的前面
			head = newnode;//头指针始终在首结点
		}else{
			for(int j = 2; j < i;j++) {
				temp = temp.next; //指向下一个结点
			}
			p = temp.next; //
			temp.next = newnode;
			newnode.next = p;
		}
		size++;
	}

图中s就是代码中newnode,图中的p就是代码中的temp。

                                      

所以当插到首和尾的时候就是此种情况的特殊情况。

//插入到首部
	public void addhead(Object e) {
		addsomewhere(e, 1);
	}
//插入到尾部
	public void addtail(Object e) {//假设有4个结点,向尾部插一个,旧相当于在第5个位置插入
		addsomewhere(e,size+1);
	}
	

3. 获取某个位置的结点元素,得到其数据域。

//查找某个结点
	private Node getNode(int i) {
		if(i<=0 || i > size) {
			throw new RuntimeException("索引越界异常:"+i);
		}
		Node temp = head;
		for(int j = 1; j < i ;j++) {
			temp = temp.next;
		}
		return temp;
	}
//查找某个位置结点元素数据域
	public Object get(int i) {
		return getNode(i).data;
	}
	

4.删除某个结点。

                                 

就是先找到要删除的结点前一个结点:a,再确认删除结点:b即:a.next,同时也确认删除结点的后一个结点:c即:a.next.next,

最后直接将a的next指向c就是,如图所示,即:a.next = c;  或是: a.next = a.next.next;结点b垃圾结点会自动回收,不用管。

//删除某个位置结点
	public void remove(int i) {
		detection2(i);
		Node temp= head;
		
		if(i == 1) {
			head = head.next;
			temp.next = null;
		}else {
			temp = getNode(i-1);//找到i的前一个
			temp.next = temp.next.next;//第i-1个结点指向第i+1个结点。
		}
		size--;
	}

5.修改某个结点的数据域。

就是先找到此结点,然后直接给数据域重新赋值即可。

​
//修改某个位置结点数据域
	public void change(Object e,int i) {
		detection2(i);//索引合格检测
		getNode(i).data = e;
	}

​

6.修改toString()方法来打印单链表。

//改写toString()打印出单链表
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder("{");
		Node temp = head;
		while(temp!=null){
			sb.append(temp.data + "-->");
			temp = temp.next;
		}
		sb.deleteCharAt(sb.length()-1); //删除最后一个字符
		sb.deleteCharAt(sb.length()-1);
		sb.setCharAt(sb.length()-1,'}');//替换最后一个字符
		return sb.toString();
	}	

四、沾一个小测试用例。

​
​
package cn.liu.Link_list;

public class Test_Linklist {
	public static void main(String[] args) {
		Link_list list = new Link_list();
		
		list.addhead("第四个结点");
		list.addhead("第三个结点");
		list.addhead("第二个结点");
		list.addhead("第一个结点");
		
		list.addtail("第1个结点");
		list.addtail("第2个结点");
		list.addtail("第3个结点");
		list.addtail("第4个结点");
			
		System.out.println(list);
		
	}
}

五、粘上我的代码。

package cn.liu.Link_list;

//定义一个单链表结点
public class Node {
	public Object data;  //数据域
	public Node next;    //next引用,相当于指针。注意它的类型为Node

	public Node(Object data) {
		super();
		this.data = data;
		this.next = null;
	}
}
package cn.liu.Link_list;

/**
 * 实现单链表
 * @author Administrator
 *
 */
public class Link_list {
	
	 Node head = null;//头指针永远指向首地址
	 public int size = 0;//记录结点的个数

	//头插法建立一个单链表
	public void initlist_head(int i) {
		detection1(i);
		for(int j = 0; j<i ; j++) {
			Node newnode = new Node(null);
			if(j==0) {
				head = newnode;
			}else {
				newnode.next = head;//插在头结点的前面
				head = newnode;//头指针始终在首结点
			}
		}
		size = i;
	}
	
	//尾插法建立一个单链表
	public void initlist_tail(int i) {
		detection1(1);
		Node tail = head;//永远指向最后一个结点
		
		for(int j = 0; j < i; j++) {
			Node newnode = new Node(null);
			if(j==0) {
				head = newnode;
			}else {
				tail.next = newnode;
			}
			tail = newnode;
		}
		size = i;
	}
	
	//插入到指定位置
	public void addsomewhere(Object e,int i) {
		detection2(i);//索引是否合格检测
		Node newnode = new Node(e);
		Node temp = head;//相当于遍历指针
		Node p = temp;//指向temp后面的结点
		
		if(i==1) {
			newnode.next = head;//插在头结点的前面
			head = newnode;//头指针始终在首结点
		}else{
			for(int j = 2; j < i;j++) {
				temp = temp.next;
			}
			p = temp.next;
			temp.next = newnode;
			newnode.next = p;
		}
		size++;
	}

	//插入到首部
	public void addhead(Object e) {
		addsomewhere(e, 1);
	}
	
	//插入到尾部
	public void addtail(Object e) {//假设有4个结点,向尾部插一个,旧相当于在第5个位置插入
		addsomewhere(e,size+1);
	}
	
	//查找某个位置结点元素数据域
	public Object get(int i) {
		return getNode(i).data;
	}
	
	//删除某个位置结点
	public void remove(int i) {
		detection2(i);
		Node temp= head;
		
		if(i == 1) {
			head = head.next;
			temp.next = null;
		}else {
			temp = getNode(i-1);//找到i的前一个
			temp.next = temp.next.next;//第i-1个结点指向第i+1个结点。
		}
		size--;
	}
	
	//修改某个位置结点数据域
	public void change(Object e,int i) {
		detection2(i);
		getNode(i).data = e;
	}
	
	//返回单链表的长度
	public  int length() {
		return size;
	}
	
	//改写toString()打印出单链表
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder("{");
		Node temp = head;
		while(temp!=null){
			sb.append(temp.data + "-->");
			temp = temp.next;
		}
		sb.deleteCharAt(sb.length()-1); //删除最后一个字符
		sb.deleteCharAt(sb.length()-1);
		sb.setCharAt(sb.length()-1,'}');//替换最后一个字符
		return sb.toString();
	}	
		
	//查找某个结点
	private Node getNode(int i) {
		if(i<=0 || i > size) {
			throw new RuntimeException("索引越界异常:"+i);
		}
		Node temp = head;
		for(int j = 1; j < i ;j++) {
			temp = temp.next;
		}
		return temp;
	}
	
	//检测索引是否合格
	private void detection1(int i) {
		if(i<=0) {
			throw new RuntimeException("索引不能小于或等于0:"+i);
		}
	}
	
	private void detection2(int i) {
		if(i<=0 || (size>0 && i>size+1)) {
			throw new RuntimeException("索引越界异常:"+i);
		}
	}

}

猜你喜欢

转载自blog.csdn.net/qq_40301026/article/details/86768703