算法合集(二)

斐波那契数列

求取斐波那契数列第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;	
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值