斐波那契数列
求取斐波那契数列第N位的值。
实现
下面写出了三种算法实现,并在注释中给出了相应的时间复杂度。
package test;
public class Fibonacci {
public static void main(String[] args) {
System.out.println(FibonacciTest(6));
System.out.println(FibonacciTest1(6));
System.out.println(FibonacciTest2(6));
}
//递归实现 时间复杂度O(2^n)
public static int FibonacciTest(int n) {
if(n == 0) {
return 0;
}else if(n == 1) {
return 1;
}else {
return FibonacciTest(n-1) + FibonacciTest(n-2);
}
}
//优化,去重递归 提前存储之后会用到的数值 时间复杂度为O(n)
public static int FibonacciTest1(int n) {
int[] arr = new int[n + 1];
return recurse(arr, n);
}
private static int recurse(int[] arr, int n) {
if(n == 0) {
return 0;
}
if(n == 1) {
return 1;
}
//去重
if(arr[n]!=0) {
return arr[n];
}
return recurse(arr,n-1) + recurse(arr,n-2);
}
//双指针迭代 只需要保存两个数字,降低空间复杂度
public static int FibonacciTest2(int n) {
if(n == 0) {
return 0;
}
if(n == 1) {
return 1;
}
int low = 0,high = 1;
for(int i = 2; i<=n;i++) {
int sum = low + high;
low = high;
high = sum;
}
return high;
}
}
排列硬币
总共n枚硬币,将他们摆成一个阶梯形状,第k行就正好有k个硬币。给定一个数字n,找出可形成完整阶梯行的总行数。
实现
算法思路有以下几种:
(1)coinTest()每次加上行上的硬币数进行判断,如果总数小于n,则继续进行,否则的话,直接返回count;
(2)coinTest2()将剩余硬币总数n-i和行(即硬币数)进行对比,直接返回行数i;
(3)coinTest3()是优化的算法,用二分查找来确认在哪一行使得之前的硬币数相加之和等于n;
(4)coinTest4()采用牛顿迭代法。x行阶梯所有硬币之和为(x*(x+1))/2,因此x=(2*n-x)/x。通过newton迭代计算出x的值。
package test;
public class CoinNum {
public static void main(String[] args) {
System.out.println(coinTest(8));
System.out.println(coinTest2(8));
System.out.println(coinTest3(8));
System.out.println(coinTest4(8));
}
public static int coinTest(int n) {
//循环内每次增加判断
//i代表行中硬币的个数,sum代表目前硬币总数,count代表满足条件的行数
int i = 0,sum = 0,count = -1;
while(sum<=n) {
count++;
i++;
sum += i;
}
return count;
}
public static int coinTest2(int n) {
//i代表行数和硬币个数
for(int i = 1;i <= n; i++) {
n = n - i;
if(n <= i) {
return i;
}
}
return 0;
}
//优化 二分查找
public static int coinTest3(int n) {
int low = 0,high = n;
while(low<=high) {
int mid = (high - low)/2 + low;
int cost = (mid * (mid + 1))/2;
if(cost == n) {
return mid;
}else if(cost > n) {
high = mid - 1;
}else {
low = mid + 1;
}
}
return high;
}
//牛顿迭代
public static int coinTest4(int n) {
if(n == 0) {
return 0;
}
return (int)sqrt(n,n);
}
private static double sqrt(double x, int n) {
double res = (x + (2*n-x)/x)/2;
if(res == x) {
return x;
}else {
return sqrt(res,n);
}
}
}
环形链表
给定一个链表,判断表中是否有环。
实现
总体来说,有两个实现方式:
(1)直接遍历链表,将其中的元素存入到set中,如果存不进去了,那就说明有环;
(2)使用快慢指针,快指针如果和慢指针相遇了,则说明存在环,如果快指针指向了null,则说明不存在环。
package test;
import java.util.HashSet;
import java.util.Set;
public class LinkCycle {
static class ListNode{
int val;
ListNode next;
public ListNode(int val,ListNode next) {
this.val = val;
this.next = next;
}
}
public static void main(String[] args) {
ListNode node5 = new ListNode(5,null);
ListNode node4 = new ListNode(4,node5);
ListNode node3 = new ListNode(3,node4);
ListNode node2 = new ListNode(2,node3);
ListNode node1 = new ListNode(1,node2);
node5.next = node3;
System.out.println(hasCycle(node1));
System.out.println(hasCycle2(node1));
}
//遍历一次链表,根据数值是否存在于set来判断 时间复杂度O(n) 空间复杂度O(n)
private static boolean hasCycle(ListNode head) {
Set<ListNode> set = new HashSet<ListNode>();
//从头指针开始查看是否有指向之前节点
while(head!=null) {
if(!set.add(head)) {
return true;
}
head = head.next;
}
return false;
}
//双指针算法 slow和quick指针,如果重叠在一起则表示有环,如果quick指向null 减少空间复杂度
private static boolean hasCycle2(ListNode head) {
// TODO Auto-generated method stub
if(head == null || head == null) {
return false;
}
ListNode slow = head;
ListNode quick = head.next;
while(slow != quick) {
if(quick == null || quick.next == null) {
return false;
}
slow = slow.next;
quick = quick.next.next;
}
return true;
}
}