记录一些数据结构的编程题好题:
1、计算二叉树深度
public class Solution {
public int TreeDepth(TreeNode pRoot) {
return pRoot == null ? 0 : Math.max(TreeDepth(pRoot.left), TreeDepth(pRoot.right)) + 1;
}
}
递归递归递归!
2、逆序输出一个链表
public class Solution {
ArrayList arrayList=new ArrayList();
public ArrayList printListFromTailToHead(ListNode listNode) {
if(listNode!=null){
this.printListFromTailToHead(listNode.next);
arrayList.add(listNode.val);
}
return arrayList;
}
}
上面的方法就是利用递归实现了栈的功能,先进后出。
自己的笨方法:
public ArrayList printListFromTailToHead(ListNode listNode) {
Stack res = new Stack();
ArrayList relist = new ArrayList();
if(listNode==null){
return null;
}
while (listNode.next != null) {
res.push(listNode.val);
listNode = listNode.next;
}
res.push(listNode.val);
while(!res.isEmpty()) {
relist.add((Integer) res.pop());
}
return relist;
}
一个前序遍历序列和一个中序遍历序列可以确定一颗唯一的二叉树。
根据前序遍历的特点, 知前序序列(PreSequence)的首个元素(PreSequence[0])为二叉树的根(root), 然后在中序序列(InSequence)中查找此根(root), 根据中序遍历特点, 知在查找到的根(root) 前边的序列为根的左子树的中序遍历序列, 后边的序列为根的右子树的中序遍历序列。 设在中序遍历序列(InSequence)根前边有left个元素. 则在前序序列(PreSequence)中, 紧跟着根(root)的left个元素序列(即PreSequence[1...left]) 为根的左子树的前序遍历序列, 在后边的为根的右子树的前序遍历序列.而构造左子树问题其实跟构造整个二叉树问题一样,只是此时前序序列为PreSequence[1...left]), 中序序列为InSequence[0...left-1], 分别为原序列的子串, 构造右子树同样, 显然可以用递归方法解决。
import java.util.*;
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(pre.length == 0||in.length == 0){
return null;
}
TreeNode node = new TreeNode(pre[0]);
for(int i = 0; i < in.length; i++){
if(pre[0] == in[i]){
node.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i+1),
Arrays.copyOfRange(in, 0, i));
node.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i+1, pre.length),
Arrays.copyOfRange(in, i+1,in.length));
}
}
return node;
}
}
跳台阶:一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
迭代:
public int JumpFloor(int target) {
if(target == 1 || target == 2) {
return target;
}
// 第一阶和第二阶考虑过了,初始当前台阶为第三阶,向后迭代
// 思路:当前台阶的跳法总数=当前台阶后退一阶的台阶的跳法总数+当前台阶后退二阶的台阶的跳法总数
int jumpSum = 0;// 当前台阶的跳法总数
int jumpSumBackStep1 = 2;// 当前台阶后退一阶的台阶的跳法总数(初始值当前台阶是第3阶)
int jumpSumBackStep2 = 1;// 当前台阶后退二阶的台阶的跳法总数(初始值当前台阶是第3阶)
for(int i = 3; i <= target; i++) {
jumpSum= jumpSumBackStep1 + jumpSumBackStep2;
jumpSumBackStep2 = jumpSumBackStep1;// 后退一阶在下一次迭代变为后退两阶
jumpSumBackStep1 = jumpSum; // 当前台阶在下一次迭代变为后退一阶
}
return jumpSum;
}
递归:
public class Solution {
public int jumpFloor(int number) {
if(number <= 0)
return 0;
else if(number == 1)
return 1;
else if(number == 2)
return 2;
else
return jumpFloor(number-1) + jumpFloor(number-2);
}
}
经常在使用递归的时候,会报错,运行超时:您的程序未能在规定时间内运行结束,请检查是否循环有错或算法复杂度过大
这是由于递归会调用很多的冗余代码,造成运行时间过长。所以迭代可以使冗余代码减少,但是递归会使得程序更加清晰。
5、逆置链表
//java版链表反转
public ListNode ReverseList(ListNode head) {
if(head==null||head.next==null){
return head;
}
//反转链表
ListNode pre=null;
ListNode next=null;
ListNode cur=head;
while(cur!=null){
next=cur.next;
cur.next=pre;//cur.next--->pre
pre=cur;
cur=next;
}
return pre;
}
6、快慢指针的应用
①判断单链表是否有环
public boolean hasCycle(ListNode head) {
if(head == null){
return false;
}
ListNode fast = head, slow = head;
while(fast != null && fast.next!=null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
return true;
}
}
//注意循环的条件是fast不为空,并且fast.next不为空
//若只有一个头结点,并且头结点的next指向头结点,仍然应该返回true
return head.next == head;
}
②
给出一个链表,若链表中有环,给出环的入口结点,否则返回null
public ListNode detectCycle(ListNode head) {
//如果有环,则两者一定是在环中相遇
ListNode meetNode = meetNode(head);
if(meetNode == null){
return null;
}
ListNode slow = head;
//当两者再次相遇时,即为链表中环的入口
while(slow != meetNode){
slow = slow.next;
meetNode = meetNode.next;
}
return slow;
}
public ListNode meetNode(ListNode head){
if(head == null){
return null;
}
ListNode meetNode;
ListNode fast = head, slow = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
meetNode = slow;
return meetNode;
}
}
//注意循环的条件是fast不为空,并且fast.next不为空
//若只有一个头结点,并且头结点的next指向头结点,仍然应该返回true
return head.next == head ? head : null;
}
相遇节点到环起始节点的距离等于链表起点到环起始节点的距离!
7、遍历一个二叉树,从上往下遍历,同级元素从左至右遍历
public class Solution {
public ArrayList PrintFromTopToBottom(TreeNode root) {
ArrayList list = new ArrayList();
if(root==null){
return list;
}
Queue queue = new LinkedList();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode treeNode = queue.poll();
if (treeNode.left != null) {
queue.offer(treeNode.left);
}
if (treeNode.right != null) {
queue.offer(treeNode.right);
}
list.add(treeNode.val);
}
return list;
}
}
8、二叉树的三种遍历方式
/**
* 先序遍历
*
* 这三种不同的遍历结构都是一样的,只是先后顺序不一样而已
*
* @param node
* 遍历的节点
*/
public static void preOrderTraverse(Node node) {
if (node == null)
return;
System.out.print(node.data + " ");
preOrderTraverse(node.leftChild);
preOrderTraverse(node.rightChild);
}
/**
* 中序遍历
*
* 这三种不同的遍历结构都是一样的,只是先后顺序不一样而已
*
* @param node
* 遍历的节点
*/
public static void inOrderTraverse(Node node) {
if (node == null)
return;
inOrderTraverse(node.leftChild);
System.out.print(node.data + " ");
inOrderTraverse(node.rightChild);
}
/**
* 后序遍历
*
* 这三种不同的遍历结构都是一样的,只是先后顺序不一样而已
*
* @param node
* 遍历的节点
*/
public static void postOrderTraverse(Node node) {
if (node == null)
return;
postOrderTraverse(node.leftChild);
postOrderTraverse(node.rightChild);
System.out.print(node.data + " ");
}
给定一个int[] numbers(C++中为vector<int>),其中第一个元素为栈顶,请返回排序后的栈。请注意这是一个栈,意味着排序过程中你只能访问到第一个元素。
import java.util.*;
public class TwoStacks {
public ArrayList twoStacksSort(int[] numbers) {
// write code here
ArrayList result = new ArrayList<>(numbers.length);
//初始化原始栈
Stack stack = new Stack<>();
int index = numbers.length - 1;
for (int i = index; i >= 0; i--) {
stack.push(numbers[i]);
}
Stack resultStack = new Stack<>();//额外的栈
while (!stack.empty()) {
if (resultStack.empty()) {
resultStack.push(stack.pop());
} else {
int a = stack.pop();
int b = resultStack.pop();
int popNum = 0;
if (a < b) {
stack.push(b);
while (!resultStack.empty() && a < (b = resultStack.pop())) {
stack.push(b);
popNum++;
}
}
if (a >= b) {
resultStack.push(b);
}
resultStack.push(a);
while(popNum>0){
resultStack.push(stack.pop());
popNum--;
}
}
}
//返回ArrayList结果
while (!resultStack.empty()) {
result.add(resultStack.pop());
}
return result;
}
}
public class Balance {
public boolean isBalance(TreeNode root) {
// write code here
if(root==null)
return true;
if(Math.abs(deepth(root.left)-deepth(root.right))>1)
return false;
else
return isBalance(root.left)&isBalance(root.right);
}
public int deepth(TreeNode root){
if(root==null)
return 0;
return Math.max(deepth(root.left),deepth(root.right))+1;
}
}
基本思想就是算深度并进行对比。
11、删除单链表的倒数第n个节点
思路:利用间隔为n的两个指针!!Brilllllllliant!!
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode start = new ListNode(0);
ListNode slow = start, fast = start;
slow.next = head;
//Move fast in front so that the gap between slow and fast becomes n
for(int i=1; i<=n+1; i++) {
fast = fast.next;
}
//Move fast to the end, maintaining the gap
while(fast != null) {
slow = slow.next;
fast = fast.next;
}
//Skip the desired node
slow.next = slow.next.next;
return start.next;
}
12、删除单链表中重复元素
我的本办法。。。(怎么这句话老出现):
public class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode headbak = head;
while(head != null && head.next!=null){
if(head.val == head.next.val){
head.next = head.next.next;
}else{
head = head.next;
}
}
return headbak;
}
}
递归。。。三行代码解决:
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head.next == null)return head;
head.next = deleteDuplicates(head.next);
return head.val == head.next.val ? head.next : head;
}