有趣的算法題-3

1.鏈表中環的入口節點

假設x爲環前面的路程(黑色路程),a爲環入口到相遇點的路程(藍色路程,假設順時針走), c爲環的長度(藍色+橙色路程)

當快慢指針相遇的時候:

此時慢指針走的路程爲Sslow = x + m * c + a
快指針走的路程爲Sfast = x + n * c + a
2 Sslow = Sfast
2 * ( x + m*c + a ) = (x + n *c + a)
從而可以推導出:
x = (n - 2 * m )*c - a
= (n - 2 *m -1 )*c + c - a
即環前面的路程 = 數個環的長度(爲可能爲0) + c - a
什麼是c - a?這是相遇點後,環後面部分的路程。(橙色路程)
所以,我們可以讓一個指針從起點A開始走,讓一個指針從相遇點B開始繼續往後走,
2個指針速度一樣,那麼,當從原點的指針走到環入口點的時候(此時剛好走了x)
從相遇點開始走的那個指針也一定剛好到達環入口點。
所以2者會相遇,且恰好相遇在環的入口點。

最後,判斷是否有環,且找環的算法複雜度爲:

時間複雜度:O(n)

空間複雜度:O(1)

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if(pHead == null || pHead.next == null)
            return null;
        ListNode slow = pHead.next;
        ListNode fast = pHead.next.next;
        while(slow != fast)
        {
            if(slow.next != null && fast.next.next != null)
            {
                slow = slow.next;
                fast = fast.next.next;
            }
            else{
                return null;
            }
        }
        fast = pHead;
        while(slow != fast)
        {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }
}

2.二叉樹的下一個節點 

給定一個二叉樹和其中的一個結點,請找出中序遍歷順序的下一個結點並且返回。注意,樹中的結點不僅包含左右子結點,同時包含指向父結點的指針。

分幾種情況討論:

1.沒有右子樹和根節點

2.沒有右子樹,是其跟節點的左節點和右節點,當是右節點是還要考慮:root節點的右子樹最右節點,root左子樹最右節點。

3.有右子樹:找到其右子樹最左的節點

class TreeLinkNode{
	int val;
	TreeLinkNode left;
	TreeLinkNode right;
	TreeLinkNode next;
	TreeLinkNode(int val)
	{
		this.val = val;
	}
}
public class nextNode {
	public TreeLinkNode getNextNode(TreeLinkNode root, TreeLinkNode pNode)
	{
		if(root == null)
			return null;
		if(pNode.right != null)
		{
			TreeLinkNode tn = pNode.right;
			while(tn.left != null)
			{
				tn = tn.left;
			}
			return tn;
		}
		if(root.next != null)
		{
			TreeLinkNode p = root.next;
			if(p.left == pNode)
				return p;
			p = pNode;
			while(p.next != null)
			{
				p = p.next;
				if(p.left == pNode)
					return p;
				pNode = p;
			}
		}
		return null;
	}
}

3.對稱的二叉樹

對稱是鏡像對稱。

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    boolean isSymmetrical(TreeNode pRoot)
    {
        if(pRoot == null)
            return true;
        return isSymmetrical(pRoot.left, pRoot.right);
    }
    public boolean isSymmetrical(TreeNode left, TreeNode right)
    {
        if(left == null && right == null)
            return true;
        if(left == null || right == null)
            return false;
        if(left.val != right.val)
            return false;
        else
        {
            return isSymmetrical(left.left, right.right) && isSymmetrical(left.right, right.left);
        }
    }
}

3.按之字型順序打印二叉樹

請實現一個函數按照之字形打印二叉樹,即第一行按照從左到右的順序打印,第二層按照從右至左的順序打印,第三行按照從左到右的順序打印,其他行以此類推。

1.用兩個棧來控制。

import java.util.ArrayList;

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
import java.util.Stack;
public class Solution {
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>();
        Stack<TreeNode> stack1 = new Stack<TreeNode>();
        Stack<TreeNode> stack2 = new Stack<TreeNode>();
        if(pRoot == null)
        {
            return list;
        }
        TreeNode pNode = pRoot;
        stack1.push(pRoot);
        while(!stack1.isEmpty() || !stack2.isEmpty())
        {
            ArrayList<Integer> l = new ArrayList<Integer>();
            if(!stack1.isEmpty())
            {
            while(!stack1.isEmpty())
            {
                pNode = stack1.pop();
                l.add(pNode.val);
                if(pNode.left != null)
                    stack2.push(pNode.left);
                if(pNode.right != null)
                    stack2.push(pNode.right);
            }
                list.add(l);
            }
            else{
            while(!stack2.isEmpty())
            {
                pNode = stack2.pop();
                l.add(pNode.val);
                if(pNode.right != null)
                    stack1.push(pNode.right);
                if(pNode.left != null)
                    stack1.push(pNode.left);
            }
                list.add(l);
            }
        }
        return list;
    }

}

4.二叉搜索樹的第K個節點(第K小的節點)

1.用中序查找,用數組記錄(數值變量只起到值傳遞,數組可以實現地址傳遞)

/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        if(pRoot == null || k <= 0)
            return null;
        TreeNode[] result = new TreeNode[1];
        KthNode(pRoot, k, new int[1], result);
        return result[0];
    }
    void KthNode(TreeNode root, int k, int[] count, TreeNode[] result)
    {
        if(root == null || result[0] != null)
        {
            return;
        }
        KthNode(root.left, k, count, result);
        count[0]++;
        if(count[0] == k)
        {
            result[0] = root;
        }
        KthNode(root.right, k, count, result);
    }


}

5.矩陣中的路徑

請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。路徑可以從矩陣中的任意一個格子開始,每一步可以在矩陣中向左,向右,向上,向下移動一個格子。如果一條路徑經過了矩陣中的某一個格子,則之後不能再次進入這個格子。 例如 a b c e s f c s a d e e 這樣的3 X 4 矩陣中包含一條字符串"bcced"的路徑,但是矩陣中不包含"abcb"路徑,因爲字符串的第一個字符b佔據了矩陣中的第一行第二個格子之後,路徑不能再次進入該格子。

1.用遞歸算法。

2.判斷邊界

public class Solution {
    public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
    {
        if(rows <= 0 || cols <= 0 || matrix == null)
            return false;
        if(str == null || str.length == 0)
            return false;
        boolean[] isVisit = new boolean[cols*rows];
        for(int i = 0; i < isVisit.length; i++)
            isVisit[i] = false;
        int index = 0;
        for(int i = 0; i < rows; i++)
            for(int j = 0; j < cols; j++)
            {
                if(isPath(matrix, rows, cols, i, j, index, str, isVisit) == true)
                    return true;
            }
        return false;
    }
    boolean isPath(char[] matrix, int rows, int cols, int row, int col, int index, char[] str, boolean[] isVisit)
    {
        if(index == str.length)
            return true;
        if(col < cols && col >= 0 && row < rows && row >= 0 && str[index] == matrix[row*cols+col] && isVisit[row*cols+col] == false)
        {
          isVisit[row*cols+col] = true;
          boolean result =  (isPath(matrix, rows, cols, row-1, col, index+1, str, isVisit) || isPath(matrix, rows, cols, row+1, col, index+1, str, isVisit)|| isPath(matrix, rows, cols, row, col-1, index+1, str, isVisit) || isPath(matrix, rows, cols, row, col+1, index+1, str, isVisit));
          if(result == true)
              return true;
          isVisit[row*cols+col] = false;
        }
         
        return false;
    }


}

6.機器人的運動範圍:

地上有一個m行和n列的方格。一個機器人從座標0,0的格子開始移動,每一次只能向左,右,上,下四個方向移動一格,但是不能進入行座標和列座標的數位之和大於k的格子。 例如,當k爲18時,機器人能夠進入方格(35,37),因爲3+5+3+7 = 18。但是,它不能進入方格(35,38),因爲3+5+3+8 = 19。請問該機器人能夠達到多少個格子?

1.這個題目主要是要拆分函數實現的功能。

2.要有一個函數將數字轉爲和,一個函數判斷是否能進入這個格子,一個函數實現遞歸。

public class Solution {
    public int movingCount(int threshold, int rows, int cols)
    {
        if(rows <= 0 || cols <= 0 || threshold <= 0)
            return 0;
        boolean[] isVisit = new boolean[rows*cols];
        for(int i = 0; i < isVisit.length; i++)
            isVisit[i] = false;
        int count = findBlank(threshold, rows, cols, 0, 0, isVisit);
        return count;
        
    }
    int findBlank(int threshold, int rows, int cols, int i, int j, boolean[] isVisit)
    {
        int count = 0;
        boolean result = isIn(threshold, rows, cols, i, j, isVisit);
        if(result == true)
        {
            count++;
            isVisit[i*cols+j] = true;
            int temp = (findBlank(threshold, rows, cols, i-1, j, isVisit) + findBlank(threshold, rows, cols, i+1, j, isVisit) 
                        + findBlank(threshold, rows, cols, i, j-1, isVisit)+findBlank(threshold, rows, cols, i, j+1, isVisit));
            count += temp;
        }
        return count;
    }
    boolean isIn(int threshold, int rows, int cols, int i, int j, boolean isVisit[])
    {
        if(i >= 0 && i < rows && j >= 0 && j < cols && isVisit[i*cols+j] == false)
            if(threshold >= isCan(i) + isCan(j))
                return true;
        return false;
    }
    int isCan(int number)
    {
       int sum = 0;
        while(number != 0)
        {
            sum += number%10;
            number /= 10;
        }
        return sum;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章