LC.148.Sort List

本文介绍了一种使用归并排序思想实现的链表排序算法,该算法可以在O(nlogn)的时间复杂度内完成排序,并且仅使用常数级别的额外空间。文章通过递归的方式将链表分为两半,再分别对这两部分进行排序,最后合并两个有序链表得到最终排序结果。
https://leetcode.com/problems/sort-list/description/
Sort a linked list in O(n log n) time using constant space complexity.
找到中点之后的处理,和 143 "Reorder List" 是一样的 : 然后把tail 传后面去
ListNode tail = mid.next ;
//cut it off
mid.next = null ;

time: o(nlogn) space: o(n): recursive with n stacks
 1 public ListNode sortList(ListNode head) {
 2         if (head == null || head.next == null) return head ;
 3         //merge sort
 4         //find the mid,
 5         ListNode mid = getMid(head) ;
 6         ListNode tail = mid.next ;
 7         //cut it off
 8         mid.next = null ;
 9         //recursive
10         ListNode firstHead = sortList(head) ;
11         ListNode secHead = sortList(tail) ;
12         //merge: same logic as merge two sorted list
13          ListNode newHead = merge(firstHead, secHead);
14          return newHead;
15     }
16 
17     private ListNode merge(ListNode firstHead, ListNode secHead) {
18         ListNode dummy = new ListNode(0) ;
19         ListNode curr = dummy ;
20         while (firstHead != null && secHead != null){
21             if (firstHead.val<secHead.val){
22                 curr.next = firstHead ;
23                 firstHead = firstHead.next ;
24                 curr = curr.next ;
25             } else{
26                 curr.next = secHead ;
27                 secHead = secHead.next ;
28                 curr = curr.next ;
29             }
30         }
31         //corner case: either one side left
32         if (firstHead!=null){
33             curr.next = firstHead ;
34         }
35         if (secHead != null){
36             curr.next = secHead ;
37         }
38         return dummy.next ;
39     }
40 
41     private ListNode getMid(ListNode head){
42         if (head == null ) return head ;
43         ListNode fast = head , slow = head ;
44         while(fast != null && fast.next != null && fast.next.next != null){
45             fast = fast.next.next ;
46             slow = slow.next ;
47         }
48         return slow ;
49     }

 

转载于:https://www.cnblogs.com/davidnyc/p/8471352.html

package com.lc.ibps.cloud.file.service.impl; import com.lc.ibps.api.base.context.CurrentContext; import com.lc.ibps.api.base.file.FileInfo; import com.lc.ibps.base.core.encrypt.EncryptUtil; import com.lc.ibps.base.core.util.BeanUtils; import com.lc.ibps.base.core.util.MapUtil; import com.lc.ibps.base.core.util.string.StringUtil; import com.lc.ibps.cloud.file.provider.FfmpegProvider; import com.lc.ibps.cloud.file.util.FtpUtil; import com.lc.ibps.cloud.redis.config.AppConfig; import com.lc.ibps.cloud.redis.utils.RedisUtil; import com.lc.ibps.components.upload.constants.FileParam; import com.lc.ibps.components.upload.constants.SaveType; import com.lc.ibps.components.upload.impl.AbstractUploadService; import com.lc.ibps.components.upload.util.UploadUtil; import org.apache.commons.io.IOUtils; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.io.*; import java.util.*; import java.util.concurrent.TimeUnit; /** * FTP文件服务实现 * * <pre> * * 构建组:iform-comp-file-server * 作者:eddy * 邮箱:xuq@ankepower.com * 日期:2018年5月22日-上午9:38:17 * 版权:广州安可信息技术有限公司版权所有 * </pre> */ public class UploadServiceFtpImpl<T extends FileInfo> extends AbstractUploadService<T> { private static final Logger logger = LoggerFactory.getLogger(UploadServiceFtpImpl.class); private static final String KEY = SaveType.ftp.name(); private static final Short FAIL_SIGN = 1; private static final String FILEINFO_FILEPATH = "fileInfo.filePath"; private static final String FILEINFO_ID = "fileInfo.id"; private CurrentContext currentContext; private AppConfig appConfig; private FTPClient ftpClient; private final static String ffmpegVideType = "mp4"; @Autowired FfmpegProvider ffmpegProvider; @Autowired public void setCurrentContext(CurrentContext currentContext) { this.currentContext = currentContext; } @Autowired public void setAppConfig(AppConfig appConfig) { this.appConfig = appConfig; } @Autowired public void setFTPClient(FTPClient ftpClient) { this.ftpClient = ftpClient; } @Override public String getSaveType() { return SaveType.ftp.name(); } @Override public T uploadFile(InputStream is, Map<String, Object> params) throws Exception { T fileInfo = fileInfoPersistenceService.initFileInfo(); //总文件md5 String fileMd5 = MapUtil.getString(params, FileParam.FILE_MD5); String originalFileName = MapUtil.getString(params, FileParam.ORIGINAL_FILE_NAME); String chunk = MapUtil.getString(params, FileParam.CHUNK); String chunkSize = MapUtil.getString(params, FileParam.CHUNK_SIZE); byte[] byteArray = IOUtils.toByteArray(is); String chunkMd5 = EncryptUtil.encryptMd5(byteArray); fileInfo = this.getFileInfo(fileInfo, params); fileInfo.setMd5(chunkMd5); fileInfo.setIsFail(FAIL_SIGN); String filePath = ""; if (StringUtil.isNotBlank(fileMd5) && StringUtil.isNotBlank(chunk)) { //pcz 分片上传 分块文件上传路径规则 为 原路径/总文件md5(前端返回的随机字符)//文件 分块文件名暂时为 原文件名 + .{chunk}. + String chunkPath = getChunkFilePath(fileMd5); String chunkFileName = getChunkFileName(chunk); filePath = FtpUtil.uploadChunkByBytes(ftpClient, "", chunkPath, chunkFileName, byteArray); fileInfo.setFilePath(filePath); } else { filePath = FtpUtil.uploadByBytes(ftpClient, "", fileInfo.getMd5(), originalFileName, byteArray); fileInfo.setFilePath(filePath); } fileInfo.setIsFail(null); //分片上传的小分片,暂时不入库,否则系统管理中,附件数据太多 if (StringUtil.isNotBlank(filePath) && StringUtil.isBlank(chunk)) { fileInfo = fileInfoPersistenceService.save(fileInfo, params); } return fileInfo; } @Override public void deleteFile(String[] deleteIds) throws Exception { // 获取对应实体类 for (int i = 0; i < deleteIds.length; i++) { String deleteId = deleteIds[i]; T fileInfo = fileInfoPersistenceService.getLoaclUpload(deleteId); if (BeanUtils.isEmpty(fileInfo)) { logger.warn("根据主键Id:{},获取不到对应的实体", deleteId); continue; } if (fileInfoPersistenceService.isUnique(fileInfo)) { FtpUtil.remove(ftpClient, "", fileInfo.getMd5(), fileInfo.getFilePath()); } fileInfoPersistenceService.deleteInfo(deleteId); } } @Override public T downloadFile(String downloadId) throws Exception { // 获取对应实体类 T loaclUpload = fileInfoPersistenceService.getLoaclUpload(downloadId); if (BeanUtils.isEmpty(loaclUpload)) { throw new Exception("根据主键Id:" + downloadId + ",获取不到对应的实体"); } byte[] readByte = FtpUtil.downloadToBytes(ftpClient, loaclUpload.getFilePath()); loaclUpload.setFileBytes(readByte); return loaclUpload; } @Override public T saveFile(Map<String, Object> params) throws Exception { T fileInfo = this.getFileInfo(params); if (isDuplicate()) { this.transferTo(fileInfo); } // 调用 使用者实现方法 实现数据持久化 return fileInfoPersistenceService.save(fileInfo, params); } @Override public boolean checkFileExists(T fileInfo) throws Exception { T info = fileInfoPersistenceService.getByIsFail(fileInfo.getMd5()); String redisKey = null; if (BeanUtils.isNotEmpty(info)) { redisKey = appConfig.getRedisKey(KEY, currentContext.getCurrentUserAccount(), info.getMd5()); RedisUtil.redisTemplate.opsForHash().put(redisKey, FILEINFO_FILEPATH, info.getFilePath()); RedisUtil.redisTemplate.opsForHash().put(redisKey, FILEINFO_ID, info.getId()); RedisUtil.redisTemplate.expire(redisKey, FtpUtil.getExpireTimeSeconds(), TimeUnit.SECONDS); return false; } try { FtpUtil.stat(ftpClient, "", fileInfo.getMd5(), fileInfo.getFilePath()); redisKey = appConfig.getRedisKey(KEY, currentContext.getCurrentUserAccount(), fileInfo.getMd5()); RedisUtil.redisTemplate.opsForHash().put(redisKey, FILEINFO_FILEPATH, fileInfo.getFilePath()); RedisUtil.redisTemplate.expire(redisKey, FtpUtil.getExpireTimeSeconds(), TimeUnit.SECONDS); return true; } catch (Exception e) { } return false; } @Override public boolean checkChunk(Map<String, Object> params) throws Exception { // TODO Auto-generated method stub return false; } @Override public T mergeChunks(Map<String, Object> params) throws Exception { // md5为分块存储最后路径/md5/ String fileMd5 = (String) params.get(FileParam.FILE_MD5); String chunkPath = FtpUtil.getChunkPath(null, true, fileMd5); String originFileName = (String) params.get(FileParam.ORIGINAL_FILE_NAME); //登陆ftpClient ftpClient = FtpUtil.connectFtp(); //获取所有分块文件 FTPFile[] files = ftpClient.listFiles(chunkPath); //获取分块名称 String[] chunkFiles = getChunkFiles(files); T fileInfo = this.getFileInfo(params); //未合并分块与未切割视频 fileInfo.setIsFail((short) 1); fileInfo = fileInfoPersistenceService.save(fileInfo, params); T finalFileInfo = fileInfo; Thread thread = new Thread(() -> { try { //异步合并分块文件 InputStream fileInputStream = FtpUtil.mergeChunkFilesAndUploadToFtp(ftpClient, finalFileInfo, chunkFiles, chunkPath, originFileName); //如果是需要视频流播放的格式,分割视屏流 if (ffmpegVideType.equals(finalFileInfo.getExt())) { ffmpegProvider.splitVideo(finalFileInfo, fileInputStream); } //合并分块与切割视频 finalFileInfo.setIsFail((short) 0); } catch (Exception e) { //合并分块成功切割视频 finalFileInfo.setIsFail((short) 1); logger.info("merge file or ffmpeg error ", e.getMessage()); } fileInfoPersistenceService.update(finalFileInfo, new HashMap<>()); }, "mergerffmpegThread"); thread.start(); return fileInfo; } /* 私有方法 */ private T getFileInfo(T fileInfo, Map<String, Object> params) throws Exception { fileInfo = UploadUtil.getFileInfo(fileInfo, params); return fileInfo; } /** * 获取新的文件信息 * * @param params * @return * @throws Exception */ private T getFileInfo(Map<String, Object> params) throws Exception { String fileMd5 = (String)params.get(FileParam.FILE_MD5); T fileInfo = fileInfoPersistenceService.initFileInfo(); String redisKey = appConfig.getRedisKey(KEY, currentContext.getCurrentUserAccount(), fileMd5); Object filePath = RedisUtil.redisTemplate.opsForHash().get(redisKey, FILEINFO_FILEPATH); if (BeanUtils.isNotEmpty(filePath)) { fileInfo.setFilePath(filePath.toString()); } return getFileInfo(fileInfo, params); } /** * 文件转存 * * @param fileInfo * @throws Exception */ private void transferTo(FileInfo fileInfo) throws Exception { InputStream readed = FtpUtil.download(ftpClient, fileInfo.getFilePath()); String filePath = FtpUtil.upload(ftpClient, "", fileInfo.getMd5(), fileInfo.getFileName(), readed); fileInfo.setFilePath(filePath); } /** private String getFileChunkPath(String fileMd5, String chunk) { return UploadUtil.getAbsolutePath(new String[] {fileMd5, chunk}); } **/ private static String[] getChunkFiles(FTPFile[] files) { // 假设分块文件名形如 "file.part1", "file.part2", ... String baseName = null; String[] chunkFiles = new String[files.length]; for(int i =0;i<files.length;i++) { String fileName = files[i].getName(); chunkFiles[i] = fileName; } //给分片排序 return Arrays.stream(chunkFiles).filter(s -> s != null).sorted( (a,b)-> { Integer aInt = Integer.parseInt(a.substring(0, a.indexOf("."))); Integer bInt = Integer.parseInt(b.substring(0, b.indexOf("."))); return Integer.compare(aInt, bInt); }) .toArray(String[]::new); } private String getChunkFilePath(String filePath) { //分片上传 分块文件上传路径规则 为 /chunk/总文件md5/文件 分块文件名暂时为 原文件名 + .{chunk}. + return "/chunk/"+filePath; } private String getChunkFileName(String chunk) { // 分块文件名暂时为 原文件名 + .{chunk}. + 如 分片1 1.1.part return chunk + "." + chunk+"." + "part"; } }
09-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值