数据结构:使用java实现单链表的构造以及基本操作

说明:本篇文章的所说的节点  结点 都是C语言的指针的意思。

1.使用内部类来实现单链表

public class LinkList {
    Node first;//指向第一个结点
    Node last;//指向最后一个结点
    class Node {//使用内部类来构造链表
        Node next;//外部类实例化内部类能访问私有属性,
        int value;//这里没有设置为私有属性是因main函数测试的时候方便。
        public Node(){};//空构造函数
        public Node(int value) {//构造函数
            this.value = value;
        }
       
    }
}

2.单链表的构建

/**
 * 使用尾插法创建链表
 * @param value
 */
    public void Construct(int value) {
        if(first == null) {
            Node current = new Node(value);
            first = current;
            last = first;//只有一个节点的时候,最后一个节点也是第一个节点
        }else {
            Node current = new Node(value);
            last.next = current;//最后一个节点指向新节点
            last = current;//更新最后一个节点的位置
        }
    }

 

3.遍历单链表

    public void travering(Node first) {//输入头节点
        Node traver = first;
        while(traver!=null) {//只要节点不为空就说明还没遍历完
            System.out.print(traver.value+"  ");
            traver = traver.next;
        }
        System.out.println();
    }

4.往单链表中插入数据(往后插~)

 /**
  * 使用尾插法插入数据
  * 当然也可以把数据往前插,用first来实现.insertNode.next = first; first = insertNode;
  * 不过我喜欢插后面~
  * @param value
  */
    public void Insert(int value) {
        Node insertNode = new Node(value);
        if(last.next == null) {//判断last是否指向了最后一个结点
            last.next = insertNode;//最后一个结点指向新结点
            last = insertNode;//更新最后一个结点
        }
    }

5.删除单链表中指定的数据

 /**
  * 删除指定数值的结点(只删除一个),但是要考虑到 删除头节点的情况
  * 删除的方式是用目标结点的前一个结点指向目标结点的下一个结点,java会自动回收删除的结点空间
  * @param value
  */
    public void Delete(int value) {
        if(first.value == value)//头节点就是目标结点情况
            first = first.next;
        else {
        Node targetNodeBefore = first;//目标结点的前一个结点
        Node targetNode = targetNodeBefore.next;//目标结点
        while(targetNode.value!=value) {
            targetNodeBefore = targetNode;
            targetNode = targetNode.next;
        }
        targetNodeBefore.next = targetNode.next;//删除目标结点
        }
    }

6.求链表的长度

 /**
  * 求链表的长度
  * 遍历一遍链表,指针挪动一次计数数值+1
  */
    
    public int ListLength() {
        Node traverNode = first;
        int length = 0;
        while(traverNode!=null) {//判断链表结束条件
            length++;
            traverNode = traverNode.next;//往下挪动
        }
        return length;
    }

7.找到倒数第 K 个结点

方法一

 /**
  * 求倒数第k个节点
  * 思路:遍历求整体长度,再定位到指定节点,时间复杂度为O(n)
  */
    public Node SearchK(int k) {
        Node kNode = first;
        int length = ListLength();//调用上面写的函数
        for(int i=0;i

方法二

思路:创建两个节点,我们称之为第一个结点和第二个结点。使它们的距离为k-1(第一个结点不动,第二个结点往后移动K-1个位置。由于第二个节点判定到链表终点的条件是traverNode.next == null的情况,但是第二个节点并不会走到null,所以第二个结点最终会停留在链表的最后一个节点,属于倒数第一个节点。因此它和第一个结点的距离就是K-1),然后两个节点同时往后面移动,直到第二个节点到达最后的结点,那么第一个结点就是倒数第k个结点。

 /**
  * 求倒数第k个节点
  * 实际上还是遍历了一遍链表。时间复杂度为O(n)
  */
    public Node SearchKplus(int k) {
        if(k==0)
            return null;
        Node kNode = first;
        Node traverNode = first;
        for(int i=0;i

8.查找链表中间结点

 /**
  * 查找链表的中间节点,不使用遍历得到链表长度
  * 
  */
   	public Node FindMid() {
		Node midNode = first;
		Node lastNode = first;
		while(lastNode.next!=null) {
			if(lastNode.next.next!=null) {
			midNode = midNode.next;
			lastNode = lastNode.next.next;
			}else {
				break;
			}
		}
		System.out.println(midNode.value);
		return midNode;
	}

9.从尾巴开始输出链表

 /**
  * 递归从尾部输出链表
  * 使用到了递归
  */
    public void Recursive(Node first) {
        if(first.next!=null)
            Recursive(first.next);
        System.out.print(first.value+"  ");
    }

10.反转单链表(使用递归方法)

/**
 * 反转链表
 */
    public Node ReverseList(Node first) {
        if(first.next == null || first == null)//作用1:如果链表长度为1或0,则直接返回就可以 
            return first;               //作用2:当递归到最后一个节点的时候返回最后一个节     
        else {
            Node newfirst = ReverseList(first.next);//此处的newfirst是反转后的链表的头节点,用来做递归的反复返回和接收,实际其值为不变的
            first.next.next = first;//这里的first是从倒数第二个节点开始,意思是当前结点的下一个节点指向当前结点
            first.next = null;//然后当前结点指向null,从而实现链表的反转
            return newfirst;
        }
    }

11.使链表成环

 /**
  * 成圆环函数
  */
    public void MkCircle(Node first) {
        last.next = first;//这种情况是整个链表就是一个环
        //last.next =  first.next;//可以通过定位到下一个节点来实现自己想要的环
    }
  

12.判断链表是否有环

思路:使用两个指针去遍历链表,第一个指针一次挪动一个位置,第二个指针一次挪动两个位置,如果改链表有环,那么他们最终会相遇

 /**
  * 判断链表是否有环
  */
    public Node Hascircle(Node head) {
        Node first = head;
        Node second = head;
        while(second.next!=null) {
            first = first.next;
            second = second.next.next;
            if(first == second)
                return first;
        }
        return null;
    }
    

13.判断环的长度

思路:拿到上个函数返回的结点,用这个结点去遍历直到返回初始位置,那就是环的长度了

/**
 * 判断环的长度
 */
    public int LengthOfCircle(Node first) {
        Node tNode = null;
        Node firstNode = null;
        Integer length = 0;
        if(Hascircle(first)!=null){
            tNode = Hascircle(first).next;//为了不和下面的判断条件起冲突,先往下挪一个位置
            length++;//对应环的长度要加一
            firstNode = Hascircle(first);//记录初始结点
            while(tNode!=firstNode) {
                length++;
                tNode=tNode.next;
            }
            return length;
        }else {
            return length;
        }
        
    }

14.找出环的初始结点

思路:同样是两个指针,第二个指针先走一个环的长度,然后两个指针再一起往下遍历,直至两个指针相等,那这就是环的初始结点

  /**
   * 找出环的初始节点
   * 思路:设置两个头节点开始遍历,第二个节点比第一个节点先走一个环的长度,然后再同时往下一步一步走,最终相等的那个节点就是环的初始节点
   */
    public Node firstCircleNode(Node first) {
        Node firstNode = first;
        Node secondNode = first;
        for(int i=0;i

15.已知一个头节点和一个节点1,在O(1)时间复杂度内删除节点1

思路:正常做我们删除节点需要得到目标节点的上一个节点,按照正常思路我们需要去遍历链表,则时间复杂度为O(n)。这里我们将节点1后面的节点复制上来,然后删除节点1后面的节点。

 public void DeleteTargetNode(Node first,Node targetNode) {
        if(targetNode.next == null)//删除节点为最后一个节点,这时只能采用常规方法来操作
            System.out.println("删除节点为最后一个节点,请采用常规方法来操作,复杂度为O(n)");
        else {
            targetNode.value = targetNode.next.value;
            targetNode.next = targetNode.next.next;
        }
    }

 

你可能感兴趣的:(数据结构)