剑指offer刷题笔记

秋招那个暑假,又想学java又想学Python,部分经典例题,有用java写的也有python 写的

1. 约瑟夫问题

public class SingleCircleLinkedListDamo {
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		SingleCircleList list1 = new SingleCircleList(20);
		list1.showList();
		System.out.println("开始游戏----------------------");
		list1.countBoy(1, 8, 20);
	}
}

//一个单向循环链表类,给一个大于1的整数,生成对应节点数的单循环链表
class SingleCircleList{
	//给的起始节点
	BoyNode firstNode=new BoyNode(1);
	public SingleCircleList(int num) {
		// TODO 自动生成的构造函数存根
		//temp指向第一个节点
		BoyNode temp=firstNode;
		if (num==1) {
			temp.next=firstNode;
			System.out.println("请传入大于1的数");
		}else {
		//	循环创建节点
			for (int i = 2; i < num+1; i++) {
				BoyNode boy = new BoyNode(i);
				temp.next=boy;
				temp=temp.next;
				//判断temp,如果temp==num-1,则temp.next指向第一个节点
				if (temp.num==num) {
					temp.next=firstNode;
					break;
				}
			}
		}
	}
	
	//遍历单链表
	public void showList() {
		BoyNode temp=firstNode;
		while (true) {
			System.out.println(temp.getNum());
			if (temp.next==firstNode) {
				break;
			}
			temp=temp.next;
		}
	}
	
	//约瑟夫问题:输入初始报数位置k,数到m出圈,一共有n个小孩在圈中
	public void countBoy(int k,int m,int n) {
		//对参数进行判断
		if (k<1||k>n) {
			System.out.println("请输入正确的参数");
		}
		//使用两个辅助变量,初始时,使其中一个指向firstNode另一个指向最后一个节点。
		//当两个辅助变量相等时就说明都已出圈
		//每次两个辅助变量都向前移动m-1次
		BoyNode temp1=firstNode;
		//temp2先移动到最后一个节点
		BoyNode temp2 = temp1;
		for (int i = 1; i < n; i++) {
			temp2=temp2.next;
		}
//		System.out.printf("%d%d",temp1.num,temp2.num);
//		同时移动两个辅助变量到起始位置
		for (int i = 1; i < k; i++) {
			temp1=temp1.next;
			temp2=temp2.next;
		}
//		System.out.printf("%d%d",temp1.num,temp2.num);
		//开始游戏
		while (true) {
			if (temp1.num==temp2.num) { //结束游戏
				System.out.printf("最后剩下的小孩编号:%d",temp1.num);
				break;
			}
			//开始报数
			for (int i = 0; i < m-1; i++) {
				temp1=temp1.next;
				temp2=temp2.next;
			}
			System.out.printf("本轮出去的小孩编号是:%d\n",temp1.num);
			temp1=temp1.next;
			temp2.next=temp1;
		}
	}
}

class BoyNode{
	public int num;
	public BoyNode next;
	public int getNum() {
		return num;
	}

	public BoyNode(int num) {
		// TODO 自动生成的构造函数存根
		this.num=num;
	}
}

2. 数组创建循环队列

class CircleArrayQueue{
	private int maxSize;
	private int front;
	private int rear;
	private int[] arr;
	
	//构造方法
	public CircleArrayQueue(int maxsize) {
		maxSize=maxsize+1;//数组中给个预留空间 
		arr=new int[maxSize];
		front=0;
		rear=0;
	}
	
	//判断队列是否满
	/*rear是从0开始递增的,假设数组长度为5,当rear为5时,数组下标越界,此时应该返回到rear=0的位置。
	 * 所以应该+1再对maxsize取余。
	 *front同理。当rear和front相邻时,此时数组为满,比如循环一轮后front下标为2,rear下标为1,此时数组满 */

	public boolean isFull() {
		return (rear+1)%(maxSize)==front;
	}
		
		//判断队列是否为空
	public boolean isEmpty() {
		return front==rear;
	}
	
	//添加 数据到队列
	public void addQueue(int num) {
		//先判断队列是否满
		if (isFull()) {
			System.out.println("队列已满,暂时无法加入数据!");
			return;
		}
		arr[rear]=num;
		rear=(rear+1)%maxSize;
	}
	
	//从队列取出数据
	public int fetchQueue() {
		//先判断队列是否为空
		if (isEmpty()) {
			throw new RuntimeException("队列为空!");
		}
		int num = arr[front];
		front=(front+1)%maxSize;
		return num;
	}
	
	//遍历队列的内容
	public  void getAllMembers () {
		//先求队列长度(索引长度)
		int queue_length=(rear+maxSize-front)%maxSize;
		for (int i = front; i < front+queue_length; i++) {
			System.out.printf("arr[%d]=%d",i%maxSize,arr[i%maxSize]);
		}
	}

3. 走迷宫问题

public class MiGongDemo {
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		int[][] migong=createMiGong();
		for (int i = 0; i < 10; i++) {
			for (int j = 0; j < 11; j++) {
				System.out.print(migong[i][j]);
			}
			System.out.println();
		}
		setWay(migong, 1, 1);
		for (int i = 0; i < 10; i++) {
			for (int j = 0; j < 11; j++) {
				System.out.print(migong[i][j]);
			}
			System.out.println();
		}
	}
	
	//走迷宫,如果到达migong[8][9]返回true。否则,如果该点为0,说明没走过,把该点设为2,
	//按照下、右、上、左的顺序调整坐标,本函数再次执行走得通就继续向下走,走不通就执行后面的顺序
	public static boolean setWay(int[][] migong,int i,int j) {
		//如果到达migong[8][9]则返回true;
		if (migong[8][9]==2) {
			return true;
		}else {
			//没达到目标点先判断该点是否为0,如果是先向下走,走之前走过的点设为2
			if (migong[i][j]==0) {
				migong[i][j]=2;
				if (setWay(migong, i+1, j)) { //如果向下能走通一直走
					return true;
				}else if (setWay(migong, i, j+1)){
					return true;
				}else if (setWay(migong, i-1, j)){
					return true;
				}else if (setWay(migong, i, j-1)){
					return true;
				}else {
					migong[i][j]=3;
					return false;
				}
			}
			else { //不是0,是1,2,3一个
				return false;
			}
		}
	}
	
	public static int[][] createMiGong() {
		int[][] migong = new int[10][11];
		//把迷宫的边置为1,其余地方为0
		for (int i = 0; i < 10; i++) {
			migong[i][0]=1;
			migong[i][10]=1;
		}
		for (int j = 0; j < 11; j++) {
			migong[0][j]=1;
			migong[9][j]=1;
		}
		migong[5][0]=1;
		migong[5][1]=1;
		migong[5][2]=1;
		return migong;
	}
} 

4.二维数组的查找

问题:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

public class TwoDimArray {
    public static void main(String[] args) {
        int[][] array = new int[2][2];
        array[0][0]=1;
        array[0][1]=2;
        array[1][0]=3;
        array[1][1]=4;
//        System.out.println(array);
        boolean res=find(8,array);
        System.out.println(res);
    }

    public static boolean find(int target,int[][] array){
        /*
        从数组的右上角开始找元素,如果这个元素大于待查找元素,则忽略这一列
        继续选取左边一个元素;如果该元素小于待查找元素,则忽略该行,继续向下查找
         */
        boolean found=false;
        int row=0;
        int column=array[row].length-1; //列索引为每一行列长度减一
        if (array!=null){
            while (row<array.length & column>=0){
                if (array[row][column]==target){
                    found=true;
                    break;
                }else if (array[row][column]>target){
                    column-=1;
                }else {
                    row+=1;
                }
            }
        }
        return found;
    }
}

5. 字符串的替换

问题:请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

public class StringOperation {
    public static void main(String[] args) {
        StringBuffer str = new StringBuffer("O DFS DSFS");
        System.out.println(str);
        String newStr=replaceSpace(str);
        System.out.println(newStr);
    }
    public static String replaceSpace(StringBuffer str) {
        //1.先遍历,统计出空格数量
        int rawLength=str.length();
        int spaceCount=0;
        for (int i = 0; i < str.length() ; i++) {
            char res = str.charAt(i);
            if (res==' '){
                spaceCount+=1;
            }
        }
        //2.把原字符串根据空格数扩容
        str.setLength(rawLength+spaceCount*2);
        //3.移动字符,遇到空格添加新字符
        /*
        使用两个指针,一个指向新字符串末尾,一个指向原字符串末尾
        同时向左移动,p1遇到空格后,p2开始插入三个新字符,并把p2向左移动3个字符,p1向左移动1个字符
         */
        int index1=rawLength-1;
        int index2=str.length()-1;
        while (index1!=index2){
            if (str.charAt(index1)==' '){
                str.setCharAt(index2,'0');
                index2-=1;
                str.setCharAt(index2,'2');
                index2-=1;
                str.setCharAt(index2,'%');
                index2-=1;
                index1-=1;
            }else {
                str.setCharAt(index2,str.charAt(index1));
                index1-=1;
                index2-=1;
            }
        }
        return str.toString();
    }
}

6. 逆序打印链表

问题:输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.*;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        Stack<Integer> stack = new Stack<Integer>();
        ArrayList<Integer> arr = new ArrayList<Integer>();
        while (listNode!=null){
            stack.push(listNode.val);
            listNode=listNode.next;
        }
        while (!stack.isEmpty()){
            arr.add(stack.pop());
        }
        return arr;
    }
}

7.重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

public class ConstructTree {
    public static void main(String[] args) {
        int[] pre={1,2,4,7,3,5,6,8};
        int[] in={4,7,2,1,5,3,8,6};
        TreeNode tree = reConstructBinaryTree(pre,in);
        tree.preOder();
    }
    public static TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        int arrLen=pre.length;
        if (arrLen==0 || pre==null || in==null){
            return null;
        }
        return constructCore(pre,in,0,arrLen-1,0,arrLen-1);
    }
    public static TreeNode constructCore(int[] pre,int[] in,int preStart,int preEnd,int inStart,int inEnd){
        //前序遍历的第一个数字是根节点
        int rootValue=pre[preStart];
        TreeNode root = new TreeNode(rootValue);
        root.left=null;
        root.right=null;
        //如果前序中序遍历的索引值已经等于数组的尾元素值,则数组已经全部用上,重建结束
        if (preStart==preEnd && inStart==inEnd){
            return root;
        }
        //在中序遍历中找的根节点的索引值
        int inRootIndex=inStart;
        while (inRootIndex<=inEnd && in[inRootIndex]!=rootValue){
            inRootIndex+=1;
        }
        //求出左右子树长度
        int leftLength=inRootIndex-inStart;
        int rightLength=inEnd-inRootIndex;
        //求左右子树在前序遍历结果中的索引
        int lefPreEnd=preStart+leftLength;
        //构建左子树
        if (leftLength>0){
            root.left=constructCore(pre,in,preStart+1,lefPreEnd,inStart,inRootIndex-1);
        }
        //构建右子树
        if (rightLength>0){
            root.right=constructCore(pre,in,lefPreEnd+1,preEnd,inRootIndex+1,inEnd);
        }
        return root;
    }
}

8.树的下一个节点

问题:给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

public class TreeNextNode {
    public static void main(String[] args) {

    }
    public static TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if (pNode!=null){
            //如果该节点有右子树,找到右子树的最左子节点
            if (pNode.right!=null){
                pNode=pNode.right;
                while (pNode.left!=null){
                    pNode=pNode.left;
                }
                return pNode;
            }
            //如果该节点没有右子树且是它父节点的左子节点,则返回它的父节点,先判断父节点是否存在,不然会空指针异常
            else if (pNode.next!=null && pNode.next.left==pNode){
                return pNode.next;
            }
            //如果该节点没有右子树且是它父节点的右子节点,则向上找到一个父节点,这个节点是该父节点左子树的一个节点
            else {
                while (pNode.next!=null && pNode.next.left!=pNode){
                    pNode=pNode.next;
                }
                return pNode.next;
            }
        }else {
            return null;
        }
    }
}


class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;
    TreeLinkNode(int val) {
        this.val = val;
    }
}

9.两个栈实现队列

问题:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

import java.util.Stack;
public class StackMakeQueue {
    public static void main(String[] args) {
        StackQueue queue = new StackQueue();
//        queue.push(2);
        System.out.println(queue.pop());
    }
}

class StackQueue{
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();

    public void push(int node) {
        stack1.push(node);
    }

    public int pop() {
        if (!stack2.isEmpty()){
            return stack2.pop();
        }else {
            while (!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
            return stack2.pop();
        }
    }
}

10. 变态跳台阶

问题:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
使用数学归纳法可以推导出,该过程为等比数列求和

public class FrogJumps {
    public static void main(String[] args) {
        System.out.println(JumpFloor_2(3));
    }

    public static int JumpFloor_2(int target) {
        if (target==1){return 1;}
        double res=Math.pow(2.0,target-1);
        return (int)res;
    }
}

11.青蛙跳台阶(斐波那契数列)

问题:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

  1. 递归解法:
public class FrogJumps {

    public static void main(String[] args) {
        System.out.println(JumpFloor_2(3));
    }

    //如果第一次跳1级,跳完有f(n-1)中跳法;如果第一次跳2级,跳完还有f(n-2)中跳法,所以f(n)=f(n-1)+f(n-2)
    public static int JumpFloor(int target) {
        if (target==1){return 1;}
        if (target==2){return 2;}
        return JumpFloor(target-1)+JumpFloor(target-2);
    }

}

12.矩形覆盖问题(斐波那契数列)

问题:我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

//矩形覆盖问题(非递归)
    public static int RectCover(int target) {
        if (target==1){
            return 1;
        }
        if (target==2){
            return 2;
        }

        int n1=1;
        int n2=2;
        int n_next=0;
        for (int i = 3; i <=target ; i++) {
            n_next=n1+n2;
            n1=n2;
            n2=n_next;
        }
        return n_next;
    }

13.二进制中1的个数

问题:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

扫描二维码关注公众号,回复: 8789801 查看本文章
public class Erjinzhi {
    public static void main(String[] args) {
        System.out.println(NumberOf1(-1));
    }

    //n与(n-1)与运算,相当于把n的二进制表示最右一位1变成0,所以不断和(n-1)做与运算即可
    public static int NumberOf1(int n) {
        int count=0;
        while (n!=0){
            count+=1;
            n=n&(n-1);
        }
        return count;
    }
}

14.数值的整数次方

问题:给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

public class PowerFunc {
    //需要考虑特殊情况,0的0或负数次方,此时返回0.0;
    //考虑指数为负数的情况
    public static void main(String[] args) {
        System.out.println(Power(2,0));
    }
    public static double Power(double base, int exponent) {
        if (base==0.0 && exponent<=0){
            return 0.0;
        }
        //如果指数是负数,指数取相反数,运算结果求倒数
        if (exponent<0){
            exponent=-exponent;
            return 1/unsignPower(base,exponent);
        }else {
            return unsignPower(base,exponent);
        }
    }

    public static double unsignPower(double base,int exponent){
        //把一个数与1做与运算,结果为0是偶数,结果为1是奇数,用位运算提高速度
        if (exponent==0){
            return 1.0;
        }
        if (exponent==1){
            return base;
        }
        //如果指数是偶数,返回a的n/2乘以a的n/2次方,此时进入递归
        else  if ((exponent&1)==0){
            return unsignPower(base, exponent>>1)*unsignPower(base, exponent>>1);
        }
        //如果指数是奇数,先把指数-1,然后和偶数的情况一样,结果再多乘以一个底数
        else {
            return base*unsignPower(base, (exponent-1)>>1)*unsignPower(base, (exponent-1)>>1);
        }
    }
}

15.调整数组奇偶元素顺序

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

public class ResortArray {
    public static void main(String[] args) {
        int[] array={2,2,5,6,7,2,1,9,5,8,8,4,6};
        reOrderArray2(array);
        for (int member:array
             ) {
            System.out.println(member);
        }
    }

    public static void reOrderArray(int [] array) {
        int p1=0;
        int p2=array.length-1;
        /*
        p1从左到右,p2从右到左,如果,p1指向奇数则移动到下一个元素处;如果p2指向偶数则移动到
        下一个元素处。p1指向的偶数和p2指向的奇数互相交换位置,当p1和p2重合时,数组重排结束
         */
        while (p1<p2){
            while ((array[p1]&1)==1){
                p1+=1;
            }
            while ((array[p2]&1)==0){
                p2-=1;
            }
            if (p1<p2){
                int temp=array[p1];
                array[p1]=array[p2];
                array[p2]=temp;
            }
        }
    }

    //保证相对位置不变的情况,用冒泡排序的思想。从第一个元素开始两两对比,如果奇数在偶数后面则交换
    public static void reOrderArray2(int [] array) {
        for (int i = 0; i < array.length-1; i++) {
            for (int p = 0; p <array.length-1 ; p++) {
                if ((array[p]&1)==0 && (array[p+1]&1)==1){
                    int temp=array[p];
                    array[p]=array[p+1];
                    array[p+1]=temp;
            }
            }
        }

    }
}

16.链表的倒数第k个节点

输入一个链表,输出该链表中倒数第k个结点。

public static ListNode FindKthToTail(ListNode head,int k) {
        /*
        使用两个指针,一开始都指向头节点,然后第1个指针向右走k-1步,
        然后两个指针一起向右走,当第1个指针到达最后一个节点时,第2个指针到达
        倒数第k个节点  */
        if (head==null || k==0){
            return null;
        }
        ListNode p1=head;
        ListNode p2=head;
        //p1前进k-1步
        for (int i = 0; i <k-1 ; i++) {
            p1=p1.next;
        }
        if (p1!=null){
            //p1和p2一起向前走直到p1到达尾结点
            while (p1.next!=null){
                p1=p1.next;
                p2=p2.next;
            }
            return p2;
        }else {
            return null;
        }

    }

17.反转链表

输入一个链表,反转链表后,输出新链表的表头。

public class ReverseListDemo {
    public static void main(String[] args) {
//        ListNode node1 = new ListNode(1);
//        ListNode node2 = new ListNode(2);
//        ListNode node3 = new ListNode(3);
//        ListNode node4 = new ListNode(5);
//        node1.add(node2);
//        node1.add(node3);
//        node1.add(node4);
        ListNode head = ReverseList(null);
        System.out.println(head.val);
    }
    /*
    用三个指针,分别指向当前节点,前一个节点,以及下一个节点,
    然后(1)先把当前节点的next指向前一个节点
    (2)把前一个节点指向当前节点
    (3)把当前节点指向下一个节点
    (4)把下一个节点指向下一个节点的next节点
    (5)重复上述过程直到当前节点的下一个节点为空
     */
    public static ListNode ReverseList(ListNode head) {
        if (head==null){
            return null;
        }
        ListNode pPre=null;
        ListNode pNode=head;
        ListNode pNext=pNode.next;
        if (pNode.next==null){
            return pNode;
        }
        while (pNode!=null){
            pNode.next=pPre;
            if (pNext!=null){
                pPre=pNode;
                pNode=pNext;
                pNext=pNext.next;
            }else {
                break;
            }
        }
        return pNode;
    }
}

18.合并两个排序的链表

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

public static ListNode mergeLinkList(ListNode pHead1,ListNode pHead2){
        if (pHead1==null){
            return pHead2;
        }

        if (pHead2==null){
            return pHead1;
        }
        //先确定新链表的头节点
        ListNode mergedHead=new ListNode(0);
        //创建一个指针初始指向头结点
        ListNode temp=mergedHead;

        while (pHead1!=null && pHead2!=null ){
            if (pHead1.val<=pHead2.val){
                temp.next=pHead1;
                pHead1=pHead1.next;
                temp=temp.next;

            }else {
                temp.next=pHead2;
                pHead2=pHead2.next;
                temp=temp.next;
            }
        }

        //遍历到其中一个节点的尾结点后会退出节点,连接剩下一个链表的剩余部分
        if (pHead1==null){
            temp.next=pHead2;
        }else {
            temp.next=pHead1;
        }
        return mergedHead.next;
    }

19.树的子结构

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

public static boolean HasSubtree(TreeNode root1,TreeNode root2) {
        boolean result=false;
        if (root1!=null && root2!=null){
            if (root1.val==root2.val){
                //如果在树1中找到了一个节点等于树2的根节点,则进一步判断,否则继续递归查找
                result = doseTree1HasTree2(root1,root2);
            }
            //如果当前相等的节点后续判断不符合,继续从左右子树找一个相同的节点
            if (result==false){
                result = HasSubtree(root1.left,root2);
            }
            if (result==false){
                result=HasSubtree(root1.right,root2);
            }

        }

        return result;
    }

//    分别对两个节点的左右子树的值进行比较
    public static boolean doseTree1HasTree2(TreeNode root1,TreeNode root2){
        //一直找到树2的子节点都没了,就可以返回找到了
        if (root2==null){
            return true;
        }

        if (root1==null){
            return false;
        }
        //遇到一个不相等的对应位置的节点,则返回false
        if (root1.val!=root2.val){
            return false;
        }

        //递归判断左右子节点,都返回true才是子树
        return doseTree1HasTree2(root1.left,root2.left) && doseTree1HasTree2(root1.right,root2.right);

    }

20 二叉树的镜像

操作给定的二叉树,将其变换为源二叉树的镜像。

public static  void Mirror(TreeNode root) {
    if (root==null){
        return;
    }

    if (root.left==null&&root.right==null){
        return;
    }

    //临时变量存放需要交换的节点
    TreeNode temp = new TreeNode(0);
    //交换左右子节点
    temp=root.left;
    root.left=root.right;
    root.right=temp;
    //递归进行下一层的交换
    if (root.left!=null){
        Mirror(root.left);
    }
    if (root.right!=null){
        Mirror(root.right);
    }

}

21 顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

class Solution():
    def main(self,matrix):
        rows=len(matrix)
        columns=len(matrix[0])

        """
        从左上角开始找打印的起点,当起点(start,start)的值start小于行或者列数其中较小的值得一半时,
        打印完当前一圈内容后继续找下一个起点,即start*2<min(rows,columns)
        """
        start=0 #定义初始的起点
        res=list()
        while start*2<min(rows,columns):
            self.printMatrixCircle(start,rows,columns,matrix,res)
            start+=1

        return res
    def printMatrixCircle(self,start,rows,columns,matrix,res):
        # 计算行列的结束位置
        endX=columns-1-start
        endY=rows-1-start

        # 从早到右打印一行
        for i in range(start,endX+1):
            res.append(matrix[start][i])

        # 从上到下打印一列,start<endY时才从上向下打印,不然会重复输出
        if start<endY:
            for i in range(start+1,endY+1):
                res.append(matrix[i][endX])

        # 从右到左打印一行,start<endX并且start<endY时才从右往左打印
        if start<endY and start<endX:
            for i in range(endX-1,start-1,-1):
                res.append(matrix[endY][i])

        # 从下到上打印一列,start<endX时才从下往上打印
        if start<endX:
            for i in range(endY-1,start,-1):
                res.append(matrix[i][start])

22.包含min函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。

class Solution():
    # 定义一个数据栈和辅助栈
    def __init__(self):
        self.dataStack=list()
        self.helpStack=list()
        self.minNum=0

    def push(self, node):
        # 当数据栈中压入新元素时,如果比当前最小值小就压入辅助栈并更新最小值
        self.dataStack.append(node)
        if len(self.helpStack)==0:
            self.helpStack.append(node)
            self.minNum=node
        else:
            if node<self.minNum:
                self.helpStack.append(node)
                self.minNum=node
            else:
                self.helpStack.append(self.minNum)

    def pop(self):
        # 从数据栈和辅助栈中分别弹出栈顶元素
        if len(self.dataStack)>0:
            self.dataStack.pop()
            self.helpStack.pop()

    def top(self):
        if len(self.helpStack)>0:
            # return self.dataStack[-1]
            return self.helpStack[-1]

    def min(self):
        return self.top()

23.栈的压入和弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

class Solution:
    def IsPopOrder(self, pushV, popV):
        """
        (1)定义一个辅助栈,判断栈顶元素是不是弹出序列的元素,如果是,弹出栈顶元素,寻找弹出序列下一个元素;
        (2)如果不是,按照压栈序列的顺序压入元素,直到压入当前弹出元素为止;
        (3)如果所有压入序列都压入了,还没有找到需要弹出的元素,则返回False
        :param pushV: 压入序列
        :param popV: 弹出序列
        :return: 压入弹出序列能否匹配
        """
        helpStack=list()
        index=0  # 压入序列的取值索引
        for target in popV:
            while index<len(pushV)+1:  # 这里数组长度不加1会导致最后一个元素没法判断
                if len(helpStack)!=0 and helpStack[-1]==target:
                    helpStack.pop()
                    break
                else:
                    if index<len(pushV):  # 这里不判断会导致数组访问越界
                        helpStack.append(pushV[index])
                        index += 1
                    else:
                        break
        if len(helpStack)==0:
            return True
        else:
            return False

24.从上往下打印二叉树

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

class Solution():
    # 返回从上到下每个节点值列表,例:[1,2,3]
    def PrintFromTopToBottom(self, root):
        """
        (1)先建立一个队列放入根节点
        (2)从队列中按顺序取出节点,并输出当前节点的值
        (3)如果当前节点有左子节点,则把左子节点放入队列
        (4)如果根节点有右子节点,则把右子节点放入队列
        (5)如果队列不为空,则继续重复第(2)步的操作
        :param root: 二叉树的根节点
        :return:
        """
        res = list()  # 存放遍历的结果
        if root==None:
            return res

        queue=list()  # 辅助遍历的队列
        queue.append(root)  # 先放入根节点
        while len(queue)>0:
            currentNode=queue.pop(0)
            res.append(currentNode.val)
            if currentNode.left !=None:
                queue.append(currentNode.left)
            if currentNode.right!=None:
                queue.append(currentNode.right)
        return res

25.二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

class Solution():
    def VerifySquenceOfBST(self, sequence):
        """
        (1)获取序列的最后一个值,该值为树的根节点
        (2)从左到右遍历序列,直到遇到一个值大于根节点的值,那么从第一个值到这个位置的值为根节点的左子树
        (3)从上面找到的值得位置,到根节点前一个值得位置,为根节点的右子树
        (4)如果左子树长度大于0,进一步递归判断
        (5)如果右子树中遍历到一个值小于了根节点,那么这个序列不是二叉搜索树的后序遍历序列,否则递归进一步判断
        :param sequence: 传入一个后序遍历序列
        :return: 判断这个树是不是二叉搜索树
        """
        if sequence==[]:
            return False

        if len(sequence)>0:
            root=sequence[len(sequence)-1]

        # 找左子树
        for i in range(len(sequence)):
            if sequence[i]>root:
                break
        # 剩下的序列为右子树,找找看是否有值小于了根节点
        for j in range(i,len(sequence)-1):
            if sequence[j]<root:
                return False

        # 递归判断左子树
        flag=True
        if i>0:
            flag=self.VerifySquenceOfBST(sequence[0:i])

        # 递归判断右子树
        if i<len(sequence)-1:
            flag=self.VerifySquenceOfBST(sequence[i:len(sequence)-1])

        return flag

26.二叉树中和为某一值的路径

输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

import copy

class Solution():
    def FindPath(self,root,expectNumber):
        """
        (1)使用一个栈存储路径上节点的val,使用一个成员变量累加求和
        (2)使用前序遍历寻找节点,如果到叶子节点了,判断累加的和是否和目标值相等
        (3)如果相等则把当前列表放入结果列表中
        (4)如果不是叶子节点继续向下寻找(递归)
        (5)当前路径已找完,在递归函数返回前先把栈顶保存的路径值弹出,继续下一个路径的查找
        :param root: 待寻找路径的树的根节点
        :param expectNumber: 待求和的数
        :return: 路劲节点值所组成的二维列表
        """
        resList = list()
        helpStack = list()
        valSum = 0
        self.FindPathMethod(root,expectNumber,resList,helpStack,valSum)
        return resList

    def FindPathMethod(self,root,expectNumber,resList,helpStack,valSum):
        if root==None:
            return [[]]
        # 放入当前节点的值(第一次传入的是根节点)
        helpStack.append(root.val)
        valSum+=root.val

        # 判断当前节点是否为叶子节点
        isLeaf=root.left==None and root.right==None
        if isLeaf and valSum==expectNumber:  # 是叶子节点且路径值和为目标值,把当前栈的内容放入结果列表
            res=copy.deepcopy(helpStack)
            resList.append(res)
        # 不是叶子节点则继续查找
        else:
            if root.left!=None:
                self.FindPathMethod(root.left,expectNumber,resList,helpStack,valSum)
            if root.right!=None:
                self.FindPathMethod(root.right,expectNumber,resList,helpStack,valSum)

        # 判断了叶子节点后栈顶弹出,累加值减去当前叶子节点值
        helpStack.pop()
        valSum-=root.val

27.复杂链表的复制

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

class RandomListNode():
    def __init__(self,x):
        self.label=x
        self.next=None
        self.random=None

    def addNode(self,node):
        if self.next==None:
            self.next=node
        else:
            self.next.addNode(node)

    def viewList(self):
        res=list()
        if self!=None:
            temp=self
        else:
            return res
        while temp!=None:
            res.append(temp.label)
            temp=temp.next
        return res


class Solution():
    def Clone(self, pHead):
        """
        (1)先对原链表的每个节点进行复制,复制的节点紧跟原节点,这样形成一个新节点和原节点交错排列的链表
        (2)遍历新链表,如果遇到有random指针的节点,则把它的下一个节点的random指针指向同等步长后的某个节点
        (3)按奇偶位置拆分链表
        :param pHead: 待复制链表的头节点
        :return: 复制后新链表的头节点
        """
        # 复制节点
        temp=pHead
        if temp==None:
            return None
        while temp!=None:
            clonedNode=RandomListNode(temp.label)
            clonedNode.next=temp.next
            temp.next=clonedNode
            temp=clonedNode.next

        # 设置随机指针
        temp=pHead
        while temp!=None:
            if temp.random!=None:
                temp.next.random=temp.random.next
            temp=temp.next.next

        # 拆分链表
        temp=pHead
        temp2=pHead.next
        clonedHead=pHead.next
        while temp!=None:
            temp.next=temp2.next
            # 最后一个节点要判空,不然temp.next.next会报错
            if temp.next!=None:
                temp2.next=temp.next.next
            else:
                temp2.next=None
            temp=temp.next
            temp2=temp2.next

        return clonedHead

28.字符串的排列

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

class Solution():
    def Permutation(self, ss):
        """
        (1)从第一个元素开始,每次把这个元素和后面序列的所有元素进行位置交换
        (2)交换完第一个元素后,再把后面序列的第一个元素与后面的序列的元素交换
        :param ss: 传入一个字符串
        :return: 字符串字符所有可能的排列的列表
        """
        if ss==None:
            return {}
        if len(ss)==1:
            return {ss}

        resList=list()  # 存放结果
        self.permutationList(ss,0,len(ss)-1,resList)

        # 去重
        result=list()
        for i in resList:
            if i not in result:
                result.append(i)
        return result


    def permutationList(self,ss,start,end,resList):
        res=list(ss)
        if start==end:
            r="".join(res)
            resList.append(r)
        else:
            # 把第一个元素依次和后面的每个元素交换位置
            for i in range(start,end+1):
                temp=res[start]
                res[start]=res[i]
                res[i]=temp

                # 把第一次交换后的结果第一个元素固定,即从start+1个元素开始再次和后面的元素依次交换
                self.permutationList("".join(res),start+1,end,resList)

29.删除链表中的重复节点

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

public static ListNode deleteDuplication(ListNode pHead){
        //遍历链表
        if (pHead==null || pHead.next==null){
            return pHead;
        }

        //用两个相邻指针操作,考虑判断空指针
        //创建一个指向头结点的固定节点pHeadPre
        //pPre初始指向pHeadPre,不能从头结点开始,不然若是头结点重复头结点无法删除
        ListNode pHeadPre = new ListNode(0);
        pHeadPre.next=pHead;

        ListNode pPre=pHeadPre;
        ListNode pNode=pHead;
        while (pNode!=null && pNode.next!=null){
            if (pNode.val==pNode.next.val) {
                //找到下一个不相等的节点
                while (pNode.next!=null && pNode.val==pNode.next.val){
                    pNode=pNode.next;
                }
                pPre.next=pNode.next;
                pNode=pPre.next;
            }
            else {
                pPre=pNode;
                pNode=pPre.next;
            }
        }

        return pHeadPre.next;
    }

30.二叉树的下一个节点

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

public static ListNode deleteDuplication(ListNode pHead){
        //遍历链表
        if (pHead==null || pHead.next==null){
            return pHead;
        }

        //用两个相邻指针操作,考虑判断空指针
        //创建一个指向头结点的固定节点pHeadPre
        //pPre初始指向pHeadPre,不能从头结点开始,不然若是头结点重复头结点无法删除
        ListNode pHeadPre = new ListNode(0);
        pHeadPre.next=pHead;

        ListNode pPre=pHeadPre;
        ListNode pNode=pHead;
        while (pNode!=null && pNode.next!=null){
            if (pNode.val==pNode.next.val) {
                //找到下一个不相等的节点
                while (pNode.next!=null && pNode.val==pNode.next.val){
                    pNode=pNode.next;
                }
                pPre.next=pNode.next;
                pNode=pPre.next;
            }
            else {
                pPre=pNode;
                pNode=pPre.next;
            }
        }

        return pHeadPre.next;
    }

31.对称二叉树

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

static boolean isSymmetrical(TreeNode pRoot)
    {
        return isSymmetrical(pRoot,pRoot);
    }

//    两次传入根节点分别从右到左遍历和从左到右遍历
    static boolean isSymmetrical(TreeNode pRoot1,TreeNode pRoot2){
        if (pRoot1==null && pRoot2==null){
            return true;
        }

        if (pRoot1==null || pRoot2==null){
            return false;
        }

        if (pRoot1.val!=pRoot2.val){
            return false;
        }

        return isSymmetrical(pRoot1.left,pRoot2.right)&&isSymmetrical(pRoot1.right,pRoot2.left);
    }
发布了5 篇原创文章 · 获赞 5 · 访问量 111

猜你喜欢

转载自blog.csdn.net/qq_41840761/article/details/104063017