数据结构和算法-常见算法题

参考:

链表反转:

https://blog.csdn.net/weixin_40807247/article/details/91435275

https://blog.csdn.net/qq_33958946/article/details/84326965

https://blog.csdn.net/qq_37117521/article/details/80808631

链表反转

理解单链表的反转(java实现)

遍历法
遍历法就是在链表遍历的过程中将指针顺序置换
在这里插入图片描述

//如何实现链表的反转
    public static Node ReverseIteratively(Node head){
        Node pre = null;
        Node next = null;
        while (head != null) {
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }

依旧是1->2->3->4
准备两个空结点 pre用来保存先前结点、next用来做临时变量
在头结点node遍历的时候此时为1结点
next = 1结点.next(2结点)
1结点.next=pre(null)
pre = 1结点
node = 2结点
进行下一次循环node=2结点
next = 2结点.next(3结点)
2结点.next=pre(1结点)=>即完成2->1
pre = 2结点
node = 3结点
进行循环…
在这里插入图片描述
递归法
总体来说,递归法是从最后一个Node开始,在弹栈的过程中将指针顺序置换的。
在这里插入图片描述
递归实质上就是系统帮你压栈的过程,系统在压栈的时候会保留现场。
我们来看是怎样的一个递归过程:1->2->3->4
程序到达Node newHead = reverse(head.next);时进入递归
我们假设此时递归到了3结点,此时head=3结点,temp=3结点.next(实际上是4结点)
执行Node newHead = reverse(head.next);传入的head.next是4结点,返回的newHead是4结点。
接下来就是弹栈过程了
程序继续执行 temp.next = head就相当于4->3
head.next = null 即把3结点指向4结点的指针断掉。
返回新链表的头结点newHead

//递归实质上就是系统帮你压栈的过程,系统在压栈的时候会保留现场
    public Node reverse(Node head) {
        //递归
        if (null == head ||null  == head.next)
            return head;
        Node temp = head.next;
        Node newHead = reverse(head.next);
        temp.next = head;
        head.next = null;
        return newHead;
    }
@Test
    public void testCase() {
        LinkedList list = new LinkedList();
        System.out.println("测试头插法");
        list.addNodeHead(4);
        list.addNodeHead(3);
        list.display();
        list.addNodeHead(2);
        list.addNodeHead(1);
        /*list.display();
        list.addNodeTail(4);
        list.display();
        System.out.println("测试某个位置删除法");
        list.deleteNode(3);
        list.display();
        System.out.println("测试删除重复数据前");
        list.addNodeHead(1);
        list.display();
        System.out.println("测试删除重复数据");
        list.deleteDuplecate1(list.head);
        list.display();
        System.out.println("测试删除重复数据1");
        list.addNodeIndex(2,1);
        list.display();
        list.deleteDuplecate(list.head);*/
        list.display();
        System.out.println("链表的反转");
        //Node node = LinkedList.ReverseIteratively(list.head);
        Node node1 = list.reverse(list.head);
        while (node1 != null){
            System.out.println(node1.data);
            node1 = node1.next;
        }
    }

在这里插入图片描述

java经典面试题:单链表反转问题详解(含递归法)

java经典面试题:单链表反转问题,有两种方法,一种为循环遍历法,一种递归法。
1、循环遍历法
在这里插入图片描述
  首先设置三个节点,把当前节点的下一节点指向它前面的节点,此时你会发现指针链会断,所以要先把它后面一个节点用nextNode保存下来,之后把节点向后移动遍历即可。
  
代码如下:

//定义单链表节点类
public class ListNode {
		int value;
		ListNode next;
		
		public ListNode() {
			this.value=0;
			this.next=null;
		}
		public ListNode(int value) {
			this.value=value;
		}
}
	
//定义链表反转方法
public ListNode reverseList(ListNode root) {
		if(root==null)return root;
		ListNode newhead=null;  //反转后的新链表头结点
		ListNode preNode = null; //前一节点
		ListNode nextNode=null; //下一节点
		ListNode curNode=root; //当前节点
		while(curNode.next!=null) {
			nextNode=curNode.next; //保存当前节点的下一节点
		    curNode.next=preNode; //使当前节点指向前一节点
		    preNode=curNode;  //把前一节点往后移
		    curNode=nextNode; //把当前节点往后移
		}
		newhead=curNode; 
		return newhead;
	}

2、递归法
如果当前节点或者当前节点的下一节点为空,直接返回该节点;
否则使该节点的后一节点指向该节点,以此递归。

public BinaryNode reverseList2(BinaryNode root) {
		if(root==null||root.next==null)return root;
		BinaryNode nextNode=root.next; //得到当前节点的下一节点
		root.next=null; //打断当前指针链
		BinaryNode re=reverseList2(nextNode); //每次递归下一节点进行反转
		nextNode.next=root; //反转指针域
		return re;
	}

使用递归反转链表

链表反转:

(图1)

把问题规模减小,并且减小的量为1

(图2)

假设我们的程序能够正常的反转:则反转后为

(图3)

反转后,1元素并没有任何操作,所以反转1的next仍然指向2,

(图4)

假设2开头的链表已经反转成功,接下来只要将2的next指向1,

(图5)

而1的next指向null即可。

(图6)

看似复杂的问题,把如此多的指针反过来指,其实只要将两个节点反过来即可。

代码如下:

package com.sise.recursion;
 
import java.util.ArrayList;
import java.util.Arrays;
 
public class LinkedListReverser {
 
    /*
     * 反转一个链表
     * head为待反转链表的头结点
     * @return 反转后的链表头结点,当然该链表也是以null结尾
     */
    public Node reverseLinkedList(Node head) {
        
        /*
         *特殊处理
         */
//        //空链表,sise==0
//        if(head==null){
//            return null;
//        }
//        //只有一个结点的时候,size==1
//        if(head.getNext()==null){
//            return head;
//        }
        
        //把上两个特殊情况合起来
        if(head==null||head.getNext()==null){
            return head;
        }
        
        
        
        //假设函数能够反转链表,返回头结点
        //---------------此处head有可能是null,head。getNext()有可能是null-----------
        Node newHead=reverseLinkedList(head.getNext());
        //此时如图4状态,1的getNext就是第二个结点2,
        //把第二结点2的next指向head则实现把2的指针指向1,如图5
        //------------此处的getNext()有可能是null------
        head.getNext().setNext(head);
        head.setNext(null);//最后指向null,如图6
        return newHead;
    }
    public static void main(String[] args) {
        LinkedListCreator creator=new LinkedListCreator();
        LinkedListReverser reverser=new LinkedListReverser();
        
        
        ArrayList arrayList=new ArrayList<>();
        
        Node.printLinkedList(
                reverser.reverseLinkedList(creator.createLinkedList(arrayList))
                );
        Node.printLinkedList(
                reverser.reverseLinkedList(creator.createLinkedList(Arrays.asList(1)))
                );
        Node.printLinkedList(
                reverser.reverseLinkedList(creator.createLinkedList(Arrays.asList(1,2,3,4,5)))
                );
 
    }
 
}
package com.sise.recursion;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
 
 
public class LinkedListCreator {
 
    /*
     * 创建一个链表
     * @return 链表的头结点,返回链表的最后一个结点的getNext()==null.
     */
    public Node createLinkedList(List<Integer> data){
        //假设传入空的List
        if(data.isEmpty()){
            return null;
        }
        
        //取出传入数据的第一个结点
        Node firstNode=new Node(data.get(0));
        //取走一个元素后,从第二个元素创建一个链表,
        //因为返回的是Node,所以用Node来接收
        //假设传入来的List有一个元素,则走到这里时sublist传入的两个参数相等
        //但是sublist函数的定义可以看到fromIndex==toIndex时,返回null
        /*
         *  <tt>fromIndex</tt> and <tt>toIndex</tt> are equal, the returned list is
         */
        //与我们期望返回值一致
//        Node headOfSublistNode=
//                createLinkedList(data.subList(1, data.size()));
//        //第一个结点的next指向规模缩小的链表返回来的头结点
//        firstNode.setNext(headOfSublistNode);
        //上面两行代码清理成如下代码
        firstNode.setNext(createLinkedList(data.subList(1, data.size())));
        return firstNode;
        
    }
    public static void main(String[] args) {
        LinkedListCreator creator=new LinkedListCreator();
        
        ArrayList arrayList=new ArrayList<>();
        
        Node.printLinkedList(
                creator.createLinkedList(arrayList)
                );
        Node.printLinkedList(
                creator.createLinkedList(Arrays.asList(1))
                );
        Node.printLinkedList(
        creator.createLinkedList(Arrays.asList(1,2,3,4,5))
    );
    }
 
}
package com.sise.recursion;
 
public class Node {
    
    private final int value;//用户定义之后就不能修改
    private Node next;
    
    public Node(int value){
        this.value=value;
        this.next=null;//这样建立出来的结点都是单点Node
    }
    
    public Node getNext() {
        return next;
    }
    public void setNext(Node next) {
        this.next = next;
    }
    public int getValue() {
        return value;
    }
    //打印函数
    public static void printLinkedList(Node head) {
        while(head!=null){
            System.out.print(head.getValue());;
            System.out.print(" ");
            head=head.getNext();
        }
        System.out.println();
    }
    
}

运行结果:

原文地址:https://www.cnblogs.com/xuwc/p/13934223.html