数据结构——java自定义单链表及反转

其实我不想写但是还是要写的的前景知识:


什么是链表
    链式存储结构是一种基于指针实现的存储结构。我们把一个数据结构和一个指针称为结点。链式存储结构用指针把相互关联的点连接起来。链式存储结构的线性表就称为链表

什么是单链表
    链表的每个结点中只包含一个指针域,叫做单链表(即构成链表的每个结点只有一个指向直接后继结点的指针)。结点结构如下:

        

关于头节点和头指针

    头结点:1.头结点是为了操作方便和统一设置的,放在第一个有数据元素的结点之前,其数据域一般无意义(有些情况下也可以用来存放链表长度,用作监视哨等)
                   2.首元结点也就是第一个有数据元素的结点,是头结点后面第一个结点
                   3.头结点不是链表必须的,头结点不算在链表长度中。

    头指针:1.在线性表的链式存储结构中,头指针是指向第一个结点的指针,假如链表有头结点,则头指针就是指向链表头节点的指针
                   2.头指针具有标识作用,常用头指针冠以链表的名字
                   3.头指针是指针,不是结点,没有数据域。
                   4.无论链表是否为空,头指针均不为空。头指针是链表必需的元素。可以理解为头指针是存储在栈中的地址,用来访问链表。
                   5.因为链表在物理存储结构上不连续,只是逻辑是连续。而头指针很大一部分意义在于,我们访问链表时需要知道链表由何处开始访问,从而由头指针指向头结点(如果没有则指向第一个有数据元素的结点),每个结点的指针依次指向下一个结点,这样可以通过指针和给定下标找到需要的结点。所以说头指针是必须的元素,没有头指针无法访问链表。

不带头结点的链表:

                                    

带头结点的链表:

                                    

                (头指针和头节点的区分可以参考:https://blog.csdn.net/hitwhylz/article/details/12305021

ok前面的说完了直接开始自定义链表

    1.首先自定义结点类。单链表是有一个个节点组成的,因此设计单链表必须要先设计结点类。结点类的变量有:指针元素,数据元素
        1.构造方法
        2.设置指针域和数据域的方法
        3.得到指针域和数据域的方法
    简单易懂,贴个代码

public class Node {

	Object data;//数据域
	Node next;//指针域
	
	public Node() {
	}
		
	public Node(Object obj) {
		super();
		this.data = obj;
	}

	//获取当前节点数据域的值
	public Object getData() {
		return data;
	}

	//设置当前节点数据域的值
	public void setData(Object data) {
		this.data = data;
	}

	//获取当前节点的指针域
	public Node getNext() {
		return next;
	}

	//设置当前节点的指针域
	public void setNext(Node next) {
		this.next = next;
	}	
}

2.单链表类
        定义头节点root(最好定义有头结点的链表,方便操作),尾节点last(也可以理解为当前结点),指针元素next(为设计方便),节点个数size
        
        方法根据自己的需求设计,如:
            1.末尾添加结点
            2.取出数据
            3.删除数据
            4.反转数据
            ......
        设计方法时并没有难度,主要是在指针域的置空,删除和赋值中弄来弄去,容易混淆。

1.首先为了节省代码量,写一个方法判断下标index是否符合要求

//单向链表
public class LinkList {
	static Node root = new Node();
	Node next;
	Node last;
	int size;

	//返回size长度的方法
	public int size() {
		return size;
	}

	// 判断index是否合格
	public void checkindex(int index) {
		if (index < 0 || index > size) {
			throw new IndexOutOfBoundsException("参数不符合要求");
		}
	}

 2.在末尾添加,假如没有结点即size=0,直接让最先创建的root头结点指向加入的新结点,置新节点为last结点。

	// 末尾添加
	public void add(Object obj) {
		next = new Node(obj);
		if (size == 0) {
			root.setNext(next);
			last = next;
		} else {
			last.setNext(next);
			last = next;
		}
		size++;
	}

3.根据index得到数据,以及更新数据。根据index在for循环中遍历得到下标为index的结点,返回数据或者设置数据

      先声明的node = root.getnext()为index=0 。当i=0时得到index=1的node。当 i=index-1时得到index的node。 

	// 得到数据
	public Object get(int index) {
		checkindex(index);
		Node node = root.getNext();
		// 找到下标为index
		for (int i = 0; i < index; i++)
			node = node.getNext();
		return node.getData();
	}
	// 设置数据
	public void update(int index, Object obj) {
		checkindex(index);
		Node node = root.getNext();
		// 找到下标为index
		for (int i = 0; i < index; i++)
			node = node.getNext();
		node.setData(obj);
	}

4.移除数据。分情况判断。

	public void remove(int index) {
		checkindex(index);		
		Node node = root.getNext();
		for (int i = 0; i < index; i++)// 找到下标为index
			node = node.getNext();		
		Node node1 = root.getNext();
		for (int i = 0; i < index - 1; i++)// 找到下标为index-1
			node1 = node1.getNext();
		
		if (index != 0) {//假如index不等于0			
			if (index != size - 1) {// 假如不是最后一个				
				Node node2 = node.getNext();// index的下一个节点
				node1.setNext(null);
				node1.setNext(node2);
			} else {// 最后一个节点				
				node1.setNext(null);
			}
			node.setData(null);
			node.setNext(null);
		} else {//假如index等于0			
			root.setNext(null);
			root.setNext(node.next);
			node.setNext(null);
			node.setData(null);
		}
		size--;
	}

5.插入。创建一个要插入的结点。找到要插入结点位置index和前一结点位置index-1 。若要插入点是最后一个,直接将原最后结点指向新加入结点。若不是,置空index-1的结点指针,再设置指向要加入结点,设置要加入结点指针指向原index结点。

public void insert(int index, Object obj) {
		checkindex(index);
		Node node = root.getNext();
		for (int i = 0; i < index; i++)		// 找到下标为index的点
			node = node.getNext();		
		Node node1 = root.getNext();
		for (int i = 0; i < index - 1; i++)// 找到下标为index-1
			node1 = node1.getNext();
		
		Node node2 = new Node(obj);// 将要插入的节点

		if (index == size - 1) {
			node.setNext(new Node(obj));
		} else {
			node1.setNext(null);
			node1.setNext(node2);
			node2.setNext(node);
		}
		size++;
	}

6.反转(递归方法反转)

1.如果是空链或者尾节点,返回结点。当然大多数情况是返回尾节点。

2.设置当前结点root.next指向前一结点,并将前一结点root的指针置空,不再指向root.next,这就完成了尾节点的反转。

3.递归。得到新的尾节点,反转。依次类推。直到前一结点为null,表示已经反转到了头指针,反转结束。

4.反转结束时,将头指针指向新的首元结点。

	public Node reverse(Node root) {
		// root看作是前一个节点,root.getNext()为当前节点,reHead是反转后新链表的头结点
		if (root == null || root.next == null) {
			// 若为空链或者当前节点在尾节点,则直接返回
			return root;
		} else {
			Node reroot = reverse(root.next);// 先反转后续节点head.next
			root.next.next = root;// 将当前节点的指针域指向前一节点
			root.next = null;// 将当前节点的指针域置空
			return reroot;// 反转后新链表的头结点
		}
	}

猜你喜欢

转载自blog.csdn.net/weixin_42621338/article/details/82180021