初级算法(七)

初级算法(七)

1、引言

不带头节点的单链表

//不带头节点的单链表
public class ListNode{
    int val;
    ListNode next;
    ListNode(){
    }
    ListNode(int val){
        this.val=val;
    }
    ListNode(int val,ListNode next){
        this.val=val;
        this.next=next;
    }
}

Java对象表示二叉树

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(){

    }
    TreeNode(int val){
        this.val=val;
    }
    TreeNode(int val,TreeNode right,TreeNode left){
        this.val=val;
        this.left=left;
        this.right=right;
    }
}

今天涉及的算法

1、合并两个有序链表变为一个有序的链表
2、回文链表:一个链表经过反转后,变为和原来的一样
3、环形链表:判断一个链表是否有环
4、二叉树的最大深度

2、合并两个有序链表变为一个有序的单链表

​ 这题属于简单级别,只需要对两个有序链表进行不断比较,较小的值放到新的链表里。直到一个链表被遍历完,退出其中的循环。然后将没有遍历完的放在新的链表。

    /**
     *合并两个有序链表变为一个有序的单链表
     * @param list1 有序单链表1
     * @param list2 有序单链表2
     * @return 合并后的有序链表
     * 执行用时:0ms,内存消耗:40.9MB
     */
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1==null){
            return list2;
        }
        if(list2==null){
            return list1;
        }
        ListNode node=new ListNode();
        ListNode curr=node;//作为记录指针
        while(list1!=null && list2!=null){
            if(list1.val<=list2.val){
                node.next=list1;
                list1=list1.next;
            }else{
                node.next=list2;
                list2=list2.next;
            }
            node=node.next;
        }
        if(list1!=null){
            node.next=list1;
        }
        if(list2!=null){
            node.next=list2;
        }
        return curr.next;
    }

3、回文链表

​ 比较简单的想法是采用堆的数据结构,这样就能从后往前遍历其中的值。


    /**
     * 方法一:使用堆栈元素存储元素值
     * 回文链表
     * @param head
     * @return
     * 执行用时:30ms,内存消耗:57.9MB
     */
    public boolean isPalindrome(ListNode head) {
        int length=length(head);
        if(length<=1)
            return true;
       ListNode temp=head;//将head的引用赋值给temp
       Stack<Integer> stack=new Stack<>();
       while(temp!=null){
           stack.push(temp.val);
           temp=temp.next;
       }
        for (int i = 0; i < length/2; i++) {  //这里只要判断一半就行,提高效率
            if(head.val!=stack.pop())
                return false;
            head=head.next;
        }
       return true;
    }
	//求单链表的长度
    int length(ListNode head){
        int i=0;
        while(head!=null){
            i++;
            head=head.next;
         }
        return i;
    }

​ 下面这种的方法,我感觉应该比较难想到,采用双指针的方式。一个是 fast 和 slow 指针。fast 指针一下走两步,slow 指针走一步。slow 指针走到中间节点的时候,将后半部分的节点进行反转。

    //反转链表
    public ListNode reverse(ListNode node){
        ListNode curr=null;
        while(node!=null){
            ListNode temp=node.next;//作为工具指针
            node.next=curr;//将原单位链表的节点,进行放在新链表的后面
            curr=node;
            node=temp;
        }
        return curr;
    }
	 /**
     * 方法二:使用fast和slow两种指针,进行寻找到中间节点的位置,将后半部分的节点进行反转
     * fast指针走两步,慢指针走一步
     * 回文链表
     * @param head
     * @return
     *执行用时:4ms,内存消耗:57.2MB
     */
    public boolean isPalindrome2(ListNode head) {
        if(head==null)
            return true;
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
        }
        if(fast!=null)
            slow=slow.next;
        slow=reverse(slow);
        while(slow!=null){
            if(head.val!=slow.val)
                return false;
            head=head.next;
            slow=slow.next;
	        }
        return true;
    }

​ 第三种方法个人感觉也是比较难理解,采用的是递归。主要对单链表进行递归调用可以调用到最后一个。这个方法还需要设置一个成员变量。

  ListNode temp3; //设置成员变量

    /**
     * 方法三:使用递归
     * 回文链表
     * @param head
     * @return
     *执行用时:13ms,内存消耗:58MB
     */
    public boolean isPalindrome3(ListNode head) {
      temp3=head;
      return check(head);

    }
    public boolean check(ListNode head){
        if(head==null)
            return true;
        boolean res=check(head.next)&&(temp3.val==head.val);//这里的check主要是找到最后的节点值
        temp3=temp3.next;
        return res;
    }

4、环形链表

​ 这题在前面的写字符串的时候,就有做一个类似的方法。其中一个简单的方法就是使用 Java 自带的数据结构 set 集合。 set 集合存储的是无序的不重复的元素。

    /**
     * 判断一个链表是不是环形链表
     * 方法一:使用Java自带的数据结构,set集合
     * @param head
     * @return
     *执行用时:4ms,内存消耗:41。8MB
     */
    public boolean hasCycle(ListNode head) {
        if(head==null)
            return false;
        ListNode temp=head;
        Set<ListNode> set=new HashSet<>();
        while(temp!=null){
            if(!set.add(temp))
                return true;
            temp=temp.next;
        }
        return false;
    }

第二种方法就是使用双指针,一个 slow 指针只走一部,一个 fast 指针只走两步。如果这个单链表是有环的,那么这两个指针一定会相遇的。

    /**
     * 方法二:使用快指针和慢指针,如果单链表有环,那么他们两者一定会相遇
     * @param head
     * @return
     * 执行用时:0ms,内存消耗:41.9MB
     */
    public boolean hasCycle2(ListNode head) {
        if(head==null)
            return false;
       ListNode fast=head;
       ListNode slow=head;
       while(fast!=null && fast.next !=null){
           fast=fast.next.next;
           slow=slow.next;
           if(fast==slow)
               return true;
       }
       return false;
    }

5、求二叉树的最大深度

对于单链表的遍历,我们可以用单链表的节点的next来达到遍历整个单链表。但是二叉树可不一样,二叉树的每一个非叶子节点,都是有两种子节点,分别是左节点和右节点。就需要我们换一种遍历方式分别是广度优先遍历和深度优先遍历。

​ 在这里求二叉树的最大深度的其中一种方法就是采用广度优先遍历。广度优先遍历,采用的一种数据结构就是队列。利用队列的先进先出的原则。

    /**
     * 方法一:使用广度优先算法
     * 返回二叉树的最大深度
     * @param root
     * @return
     * 执行用时:1ms,内存消耗:40.9MB
     */
    public int maxDepth(TreeNode root) {
        if(root ==null)
            return 0;
        TreeNode temp=root;
        LinkedList<TreeNode> queue=new LinkedList<>();
        queue.addLast(root);
        int count =0;
        while(!queue.isEmpty()){
           int size =queue.size();
           count++;
           while(size>0){
               TreeNode pop = queue.removeFirst();
               size--;
               if(pop.left!=null){
                   queue.addLast(pop.left);
               }
               if(pop.right!=null){
                   queue.addLast(pop.right);
               }
           }
        }
   return count;
 }

第二种方法就是使用深度优先遍历算法,这种遍历算法采用的是堆栈的数据结构。采用两个堆栈,一个堆栈存储树节点,另一个堆栈结构则存储的是二叉树的最大深度。当存储树节点的堆栈出栈的时候,存储树深度的堆栈也出栈。进栈也是这样的。

    /**
     * 方法二:深度优先搜索
     * @param root
     * @return
     * 使用两个堆栈的方式,一个堆栈存储节点,另一个堆栈存储层数
     * 执行用时3ms,内存消耗:41.3MB
     */
    public int maxDepth2(TreeNode root) {
      if(root == null)
          return 0;
        Stack<TreeNode> stack1=new Stack<>();
        Stack<Integer> stack2=new Stack<>();
        int max=0;
        stack1.push(root);
        stack2.push(1);
        while(!stack1.isEmpty()){
            TreeNode pop = stack1.pop();
            int count = stack2.pop();
            max=Math.max(max,count);
            if(pop.left!=null){
                stack1.push(pop.left);
                stack2.push(count+1);
            }
            if(pop.right!=null){
                stack1.push(pop.right);
                stack2.push(count+1);
            }
        }
        return  max;
    }

第三种方法,求二叉树的最大深度其实,其实求二叉树左子树和右子树的最大深度+1。这就是递归。

    /**
     * 方法三:递归,求某个二叉树的最大深度,就是求它左子树和右子树的最大值+1
     * @param root
     * @return
     * 执行用时:0ms,内存消耗:40.8MB
     */
    public int maxDepth3(TreeNode root) {
        if(root == null)
            return 0;
        else{
            return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
初级算法 - Python实现 初级算法是帮助入门算法的一部分,它们旨在帮助初学者掌握算法数据结构,并提高编程能力。在Python中,我们可以使用各种排序算法来实现初级算法。 常见的初级排序算法包括选择排序、插入排序、冒泡排序和希尔排序。选择排序是一种简单直观的排序方法,它通过不断选择最小的元素并将其放在正确的位置上来排序。插入排序则是通过逐步构建有序序列来排序,将每个元素插入到已排序的序列中的适当位置。冒泡排序通过重复比较相邻的元素并交换它们的位置来排序。希尔排序是一种改进的插入排序算法,通过将比较的元素间隔逐步缩小来提高效率。 选择合适的排序算法取决于具体的应用场景和数据规模。在Python中,我们可以根据需要选择相应的算法来实现初级排序。这些算法在性能上可能有所差异,因此在实际应用中需要根据情况进行选择。 总结起来,初级算法在Python中可以通过实现选择排序、插入排序、冒泡排序和希尔排序等常见的排序算法来实现。这些算法可以帮助初学者掌握基本的排序方法,并在实践中提高编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [用 Python 学习算法初级排序算法](https://blog.csdn.net/wangs0622/article/details/78690519)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [力扣初级算法(Python)](https://blog.csdn.net/qq_41068877/article/details/121952963)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值